【问题标题】:How can I create "C Blocks" when using FFI?使用 FFI 时如何创建“C 块”?
【发布时间】:2015-04-17 07:54:08
【问题描述】:

我在 OS X 上使用CoreFoundation 框架,但我不知道如何在 Rust 中映射这个函数:

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

最后一个参数是void(^block)(void)——如何创建这种类型的参数?

【问题讨论】:

    标签: rust ffi


    【解决方案1】:

    简短的,可能有用的答案:有 block 板条箱,看起来它可以完成这项工作。

    简短而无益的回答:据我所知,Rust 不支持 Apple 的块扩展。没有等效的 Rust 类型,假设您要调用一个需要一个块的 API。

    答案更长,没有什么帮助:根据我从some Clang documentation on the Apple Block ABI 收集到的信息,void(^)(void) 的大小与常规指针相同。

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

    以下内容未经测试(我没有 Mac),但至少应该让您朝着正确的方向前进。另外,我正在标记这个社区 wiki,以便可以对其进行测试的任何人都可以在需要时修复它。

    在锈中:

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

    【讨论】:

      猜你喜欢
      • 2011-08-20
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2016-08-11
      • 2021-04-27
      相关资源
      最近更新 更多