2015-07-17 2 views
6
codice

esempio riportato di seguitoCome controllare se puntatore a funzione passati dalla C è NULL non

La parte Rust:

#[no_mangle] 
pub extern fn call_c_function(value: i32, fun: fn(i32) -> i32) -> i32 { 
    fun(value) 
} 

E la parte C:

int32_t call_c_function(int32_t value, int32_t (*fun)(int32_t)); 

int32_t triple(int32_t x) 
{ 
    return x*3; 
} 

int main(int argc, char *argv[]) 
{ 
    int32_t value = 3; 
    int32_t result = call_c_function(value, triple); 

    printf("%d tripled is %d\n", value, result); 

    call_c_function(0, NULL); // Crash here 

    return EXIT_SUCCESS; 
} 

Naturalmente seconda chiamata di call_c_function si bloccherà. Il compilatore di ruggine non si lamenterà del codice non sicuro all'interno di call_c_function, perché dal punto di vista della ruggine questo codice è sicuro. Inoltre non è permesso di scrivere semplicemente:

if !fun.is_null() { 
    fun(value) 
} 

perché fun tipo è fn(i32) -> i32 (non è un puntatore).

Quindi la mia domanda è: come posso proteggere call_c_function contro il puntatore NULL? C'è un modo per verificare se il callback passato da C non è valido?

Forse dovrò cambiare la definizione di call_c_function?

+0

Sì, basta controllare se divertente == NULL. O zero, se NULL non è una cosa in ruggine. Intendiamoci, non conosco molto bene la ruggine, ma è così che lo faresti in C. – rost0031

+2

Vedi qui: http://doc.rust-lang.org/book/ffi.html#the-%22nullable-pointer -ottimizzazione% 22 – Adrian

+0

@Adrian Thx, questo è quello che stavo cercando! – ldanko

risposta

9

È possibile utilizzare Option<...> per rappresentare i puntatori di funzione nullable. Non è corretto avere un valore NULL per un valore di tipo fn(...) in modo che il wrapper Option sia necessario per casi come questo.

Per esempio,

#[no_mangle] 
pub extern fn call_c_function(value: i32, fun: Option<fn(i32) -> i32>) -> i32 { 
    if let Some(f) = fun { f(value) } 
} 

Tuttavia, c'è il punto in più: fun è una funzione C, ma il tipo fn(...) è una funzione della ruggine. Non sono direttamente compatibili (ad esempio le convenzioni di chiamata diversi), si ha la necessità di utilizzare il tipo di extern "C" fn(...) (aka extern fn(...)) quando interagisce con puntatori a funzione C:

#[no_mangle] 
pub extern fn call_c_function(value: i32, fun: Option<extern fn(i32) -> i32>) -> i32 { 
    if let Some(f) = fun { f(value) } 
}