2015-04-17 23 views
6

Sto lavorando con il quadro CoreFoundation su OS X, ma non so come mappare questa funzione in Rust:Come posso creare "blocchi C" quando si utilizza FFI?

void CFRunLoopPerformBlock(CFRunLoopRef fl, CFTypeRef mode, void (^block)(void)); 

L'ultimo parametro è void(^block)(void) - Come posso creare argomenti di questo tipo?

risposta

7

Risposta breve, probabilmente utile: c'è la cassa block, che sembra possa fare il lavoro.

Risposta breve, non utile: Nella misura in cui sono a conoscenza, Rust non ha alcun supporto per l'estensione del blocco di Apple. Non esiste un tipo di Rust equivalente, supponendo che tu voglia chiamare un'API che si aspetta un blocco.

più lungo, risposta marginalmente meno inutile: Da quello che ho potuto capire da some Clang documentation on the Apple Block ABI, void(^)(void) sarebbe la stessa dimensione di un puntatore normale.

Come tale, il mio consiglio è il seguente: trattare i blocchi come valori opachi, di dimensioni puntatore. Per invocarne uno, scrivi una funzione in C che la chiama per te.

Quanto segue non è stato verificato (non ho un Mac), ma dovrebbe almeno andare nella giusta direzione. Inoltre, sto marcando questo wiki della comunità in modo che chiunque possa provare può ripararlo se necessario.

A Rust:

// These are the "raw" representations involved. I'm not using std::raw 
// because that's not yet stabilised. 
#[deriving(Copy, Clone)] 
struct AppleBlock(*const()); 

#[deriving(Copy, Clone)] 
struct RustClosure(*const(), *const()); 

// Functions that we need to be written in C: 
extern "C" { 
    fn rust_closure_to_block(closure_blob: RustClosure) -> AppleBlock; 
    fn block_release(block_blob: AppleBlock); 
} 

// The function that the C code will need. Note that this is *specific* to 
// FnMut() closures. If you wanted to generalise this, you could write a 
// generic version and pass a pointer to that to `rust_closure_to_block`. 
extern "C" fn call_rust_closure(closure_blob: RustClosure) { 
    let closure_ref: &FnMut() = unsafe { mem::transmute(closure_blob) }; 
    closure_ref(); 
} 

// This is what you call in order to *temporarily* turn a closure into a 
// block. So, you'd use it as: 
// 
//  with_closure_as_block(
//   || do_stuff(), 
//   |block| CFRunLoopPerformBlock(fl, mode, block) 
// ); 
fn with_closure_as_block<C, B, R>(closure: C, body: B) -> R 
where C: FnMut(), B: FnOnce(block_blob) -> R { 
    let closure_ref: &FnMut() = &closure; 
    let closure_blob: RustClosure = unsafe { mem::transmute(closure_ref) }; 
    let block_blob = unsafe { rust_closure_to_block(closure_blob) }; 
    let r = body(block_blob); 
    unsafe { block_release(block_blob) }; 
    r 
} 

In C:

typedef struct AppleBlock { 
    void *ptr; 
} AppleBlock; 

typedef struct RustClosure { 
    void *ptr; 
    void *vt; 
} RustClosure; 

void call_rust_closure(RustClosure closure_blob); 

AppleBlock rust_closure_to_block(RustClosure closure_blob) { 
    return (AppleBlock)Block_copy(^() { 
     call_rust_closure(closure_blob); 
    }); 
} 

// I'm not using Block_release directly because I don't know if or how 
// blocks change name mangling or calling. You might be able to just 
// use Block_release directly from Rust. 
void block_release(AppleBlock block) { 
    Block_release((void (^)(void))block); 
} 
Problemi correlati