2015-04-17 33 views
6

我与OS X的CoreFoundation框架内工作,但我不知道该怎么给这个函数在拉斯特地图:如何在使用FFI时创建“C块”?

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

最后一个参数是void(^block)(void) - 我怎样才能创建此类型的变量?

回答

7

短,可能有帮助的答案:有block箱,看起来它可能会做这项工作。

简短而无益的答案:就我所知,Rust对苹果的块扩展没有任何支持。没有相应的Rust类型,假设你想调用一个需要块的API。

较长,稍微少无益的答案:从我可以从some Clang documentation on the Apple Block ABI收集,void(^)(void)将大小作为一个普通指针一样。

因此,我的建议如下:将块视为不透明的指针大小的值。为了调用它,在C中写一个函数来为你调用它。

以下是未经测试(我没有Mac),但至少应该让你朝着正确的方向前进。此外,我正在标记这个社区维基,以便任何人可以测试它可以解决它,如果需要的话。

在锈:

// 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 
} 

在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); 
} 
相关问题