注册具有不同功能签名的多个FFI回调

时间:2018-01-15 16:49:34

标签: rust

我试图在Rust和C动态库之间编写一些绑定代码。在Rust书的帮助下,我能够链接动态库并调用外部函数。

我的问题是我必须向C库注册一些回调,但我无法做到:

#[link(name = "MyLibrary")]
extern "C" {
    fn Auth_RegisterCallback(
        auth: size_t,
        name: *const c_char,
        function: &fn(size_t) -> i32,
        param: size_t,
    );
}

fn Authenticate() {
    unsafe {
        Auth_RegisterCallback(
            auth,
            CString::new("OnAuthBegin").unwrap().as_ptr(),
            OnAuthBegin,
            param,
        )
    };
    unsafe {
        NowAuth_RegisterCallback(
            auth,
            CString::new("OnSuccess").unwrap().as_ptr(),
            OnSuccess,
            param,
        )
    };
}

fn OnAuthBegin(auth: size_t) -> i32 {
    println!("Auth begin");
    return 1;
}

此代码工作正常,因为extern块中的Auth_RegisterCallback期望参数中的函数具有size_t类型的一个参数。

我的问题是我有多个回调来注册不同的签名,如:

fn OnSuccess(context: size_t, success: size_t) -> i32 {
    println!("Success");
    return 1;
}

有没有办法传递参数中的函数,即使它们的签名不同?

1 个答案:

答案 0 :(得分:1)

如果您的FFI采用任意指针,那就是您必须表示您的功能。使用libc::c_void并转换函数指针:

extern crate libc;

use libc::{c_char, size_t, c_void};

use std::ffi::CString;

extern "C" {
    fn Auth_RegisterCallback(
        auth: size_t,
        name: *const c_char,
        function: *const c_void,
        param: size_t,
    );
}

fn authenticate() {
    let auth = unimplemented!();
    let param = unimplemented!();

    unsafe {
        Auth_RegisterCallback(
            auth,
            CString::new("OnAuthBegin").unwrap().into_raw(), // Memory leak
            on_auth_begin as *const _,
            param,
        );
        Auth_RegisterCallback(
            auth,
            CString::new("OnSuccess").unwrap().into_raw(), // Memory leak
            on_success as *const _,
            param,
        )
    };
}

fn on_auth_begin(auth: size_t) -> i32 {
    println!("Auth begin");
    return 1;
}

fn on_success(context: size_t, success: size_t) -> i32 {
    println!("Success");
    return 1;
}

您可能希望通过在FFI函数周围创建多个单独的包装来添加某些类型的安全性:

fn register_begin_callback(f: fn(ctx: size_t, arg1: u8)) {
    let auth = unimplemented!();
    let param = unimplemented!();

    unsafe {
        Auth_RegisterCallback(
            auth,
            CString::new("OnAuthBegin").unwrap().into_raw(), // Memory leak
            f as *const _,
            param,
        );
    }
}

另见: