前往小程序,Get更优阅读体验!
立即前往
首页
学习
活动
专区
工具
TVP
发布
社区首页 >专栏 >Rust FFI 编程 - 手动绑定 C 库入门 06

Rust FFI 编程 - 手动绑定 C 库入门 06

作者头像
MikeLoveRust
发布2020-07-07 12:01:23
1.2K0
发布2020-07-07 12:01:23
举报
文章被收录于专栏:Rust语言学习交流

我们继续研究 Rust 与 C 之间传递回调函数,上一篇使用的是函数指针,本文介绍如何使用闭包来实现这个问题。我们回顾下目标:

  1. 在 C 端有个函数,有个回调函数作为参数;
  2. 在 Rust 端,有个闭包;并在主函数中,要使用定义的闭包调用 C 端的那个函数。
闭包

我们知道 Rust 的闭包不仅是一个函数指针,这意味着不能使用它作为回调函数的函数指针直接传递给 C 端。

同时我们也知道 Rust 中的所有的闭包都实现了由标准库提供的 trait FnFnMutFnOnce 中的一个。闭包语法 || {} 实际上是 Fn 系列 trait 的语法糖,Rust 会为“环境”创建一个结构体,impl其中合适的一个 trait,并使用它。

因此,从理论上讲,我们应该能够通过将闭包“拆分”为两部分,匿名类型的实例数据和某种类似call()方法的函数。这样我们可以获取其中函数部分的指针,从而实现将闭包传递给 C 端代码。

具体的方法就是:首先创建一个泛型 hook 函数,该函数和回调函数的参数列表一样,在其中构建并调用闭包。然后创建一个 getter 函数,该函数接受闭包的引用作为参数,返回一个函数指针。

我们沿用上篇设计的示例,稍作修改:

  1. C 端,sum_square_cb 函数,接收两个整型参数 a, b,一个函数指针,一个void *
  2. Rust 端,定义一个 getter 函数 get_callback
  3. Rust 端,定义一个闭包,被调用时更新数据 user_data;
  4. Rust 端,调用 C 中定义的 sum_square_cb

好,代码部分 C 端保持不变,我们看 Rust 端的两个函数hookget_callback,代码如下:

代码语言:javascript
复制
// ffi/example_10/src/main.rs

unsafe extern fn hook<F>(result: c_int, user_data: *mut c_void)
where
    F: FnMut(c_int),
{
    let closure = &mut *(user_data as *mut F);
    closure(result);
}

pub fn get_callback<F>(_closure: &F) -> SumSquareCB
where
    F: FnMut(c_int),
{
    hook::<F>
}

由于我们希望闭包能改变其环境,所以在定义hook函数时,我们限定闭包实现为FnMut并以c_int作为参数。在函数体中的这一句 let closure = &mut *(user_data as *mut F); ,先通过把 *mut c_void 指针转换成 *mut F 指针,然后用 * 取得它的数据块,并使用 &mut 取得可变引用 ,最后调用闭包。

同时get_callback函数中仅有的语句,hook::<F>,我们使用了一个叫做 turbofish ::<> 的语法,用来显式指定返回F类型的hook函数。

接下来我们 Rust 端的主函数,代码如下:

代码语言:javascript
复制
fn main() {
    let mut record = SumRecord::default();
    
    unsafe {
        let mut closure = |result: c_int| {
            record.total += result;
            record.calls += 1;
        };
        let callback = get_callback(&closure);

        sum_square_cb(1, 2, callback, &mut closure as *mut _ as *mut c_void);

        sum_square_cb(3, 4, callback, &mut closure as *mut _ as *mut c_void);
    }

    println!("The sum is {:?}", record);
}

这个 let mut closure 语句意味着 closure 包含一个匿名函数的 定义,而不是调用后的 返回值,该函数接受一个c_int类型的参数。我们使用闭包的原因是需要事先定义一段代码,并在之后的某个时候才实际调用它。这里我们将期望调用的代码储存在了 closure 中。

接着我们调用get_callback,其中有一点非常重要,它返回的函数指针只能在传入的同一闭包上使用。因为我们定义hook函数时在未进行任何类型检查的情况下,将user_data直接转换为该闭包类型的指针。

同时在调用 C 端函数sum_square_cb时,我们通过获取闭包变量 closure的可变引用,并进行两次指针转换,将其强制转换为 void * 指针来获取其数据。其中我们使用了_占位符由 Rust 编译器来推断该位置的闭包类型。

小结

我们使用 Rust 调用 C 时,要在两者之间传递闭包,可以通过将闭包“拆分”出函数指针来完成这个操作。

本篇的完整代码位于:https://github.com/lesterli/rust-practice/tree/master/ffi/example_10

本文参与 腾讯云自媒体同步曝光计划,分享自微信公众号。
原始发表:2020-07-02,如有侵权请联系 cloudcommunity@tencent.com 删除

本文分享自 Rust语言学习交流 微信公众号,前往查看

如有侵权,请联系 cloudcommunity@tencent.com 删除。

本文参与 腾讯云自媒体同步曝光计划  ,欢迎热爱写作的你一起参与!

评论
登录后参与评论
0 条评论
热度
最新
推荐阅读
目录
  • 闭包
  • 小结
领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档