How can I create "C-blocks" when using FFI?

I work with the CoreFoundation on OS X, but I don’t know how to map this feature in Rust:

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

The final parameter is void(^block)(void) - how can I create arguments of this type?

+6
source share
1 answer

A short, probably useful answer : there is a block box that looks like it can do the job.

A short, useless answer . To my knowledge, Rust does not support Apple extension support. There is no equivalent type of Rust, assuming you want to call the API that the block expects.

A longer, slightly less unhelpful response . From what I can compile from some Clang documentation on Apple Block ABI , void(^)(void) will be the same size as a regular pointer.

So my advice is this: treat the blocks as opaque pointer size values. To call it, write a function in C that calls it for you.

The following has not been verified (I don't have a Mac), but at least you will go in the right direction. I also mark this community wiki, so anyone who can test it can fix it if necessary.

In the rust:

 // These are the "raw" representations involved. I'm not using std::raw // because that 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); } 
+7
source

Source: https://habr.com/ru/post/985328/


All Articles