2014-09-30 2 views
13

Potrei non aver descritto correttamente il titolo della mia domanda, per favore modificalo se necessario.Creare un'interfaccia per i puntatori di funzioni C in Rust

Sto cercando di cassa di un'interfaccia Rust a LXC library, che è scritto in C.

ho chiamato con successo funzioni semplici come lxc_get_version o lxc_container_new ma non posso accedere alle funzioni descritte in struct lxc_container blocco.

Ecco una parte del mio codice:

#[link(name = "lxc")] 
extern { 
    // LXC part 
    fn lxc_get_version() -> *const c_char; 
    fn lxc_container_new(name: *const c_char, configpath: *const c_char) -> LxcContainer; 

    // LXC container parts 
    fn is_defined(container: &LxcContainer) -> bool; 
} 

E qui è un errore:

note: test.o: In function `LxcContainer::is_defined::heb2f16a250ac7940Vba': 
test.0.rs:(.text._ZN12LxcContainer10is_defined20heb2f16a250ac7940VbaE+0x3e): undefined reference to `is_defined' 

EDIT: Sono riuscito che funzioni all'interno C struct è chiamato puntatori a funzione. Ho provato a google qualcosa come "Rust C function pointer", ma senza fortuna.

risposta

20

quando si vede qualcosa di simile (in C):

struct S { 
    void (*f)(int, long) 
} 

significa che struct S contiene un campo denominato f che è un puntatore a una funzione. Ciò non significa che la libreria stessa esponga una funzione denominata f. Ad esempio, questo è valido:

void some_function_1(int x, long y) { ... } 

void some_function_2(int a, long b) { ... } 

int main() { 
    struct S s1; s1.f = some_function_1; 
    struct S s2; s2.f = some_function_2; 
} 

Qui esempio struct s1 contiene un puntatore a some_function_1, e s2 contiene un puntatore a some_function_2.

Quando si scrive l'associazione FFI in Rust per alcune librerie C, di solito si definiscono le controparti di Rust per le strutture C. Alcuni strumenti come rust-bindgen possono persino farlo automaticamente. Nel tuo caso si dovrà scrivere qualcosa del genere:

#[repr(C)] 
struct LxcContainer { 
    name: *mut c_char, 
    configfile: *mut c_char, 
    // ... 
    numthreads: c_int, 
    // ... 
    is_defined_f: extern fn(c: *mut LxcContainer) -> bool, 
    state_f: extern fn(c: *mut LxcContainer) -> *const c_char, 
    // ... 
} 

Cioè, i tipi puntatore a funzione C strani dall'aspetto corrispondono a extern fn tipi puntatore a funzione di Rust. È inoltre possibile scrivere extern "C" fn(...) -> ..., ma il qualificatore "C" è l'impostazione predefinita, quindi non è richiesta.

Si dovrà scrivere qualcosa di simile a chiamare queste funzioni:

impl LxcContainer { 
    fn is_defined_f(&mut self) -> bool { 
     unsafe { 
      (self.is_defined_f)(self as *mut LxcContainer) 
     } 
    } 
} 

È necessario lanciare un riferimento a un puntatore crudo e è anche necessario per avvolgere self.is_defined_f tra parentesi per disambiguare tra il metodo di chiamata e accesso al campo.

È possibile trovare ulteriori informazioni su FFI in Rust here. I puntatori di funzione sono spiegati molto brevemente lì, però.

+0

Grazie per la risposta, ora posso accedere ai puntatori di funzione. Ho scritto una struttura per la rappresentazione in C e un wrapper per la rappresentazione di Rust attorno ad essa. Sfortunatamente non posso effettuare una chiamata a 'is_defined' perché non riesco a passare la struct LxcContainer a funzionare come * LxcContainer. Si verifica un errore di dereference. Potresti aggiungere qualche esempio di puntatore a funzione che chiama nella tua risposta? Grazie. – bbrodriges

+0

@bbrodriges, mi dispiace per la risposta in ritardo, ma ho aggiornato la mia risposta con un esempio di come chiamare le funzioni memorizzate nei campi struct. –