首页
学习
活动
专区
工具
TVP
发布
精选内容/技术社群/优惠产品,尽在小程序
立即前往

Rust FFI 编程-bindgen 使用示例

当我们拥有一组具有良好声明的头文件时,自己定义 C 库的 Rust FFI 绑定函数是毫无意义的。我们可以使用  这种工具从 C 库的头文件生成 Rust FFI 绑定函数。然后,我们运行一些测试代码以验证其是否正常运行,并对它们进行调整,直到正确为止。

本文我们将通过一个示例,讨论如何使用  将 C 库中的函数公开给 Rust。我们的目标是创建一个 crate 项目,其中包含一个文件,该文件代表 C 库的公共 API(包括函数,结构体,枚举等),然后通过将该 crate 导入其它项目中来调用原 C 库的功能。

上一篇我们介绍了使用 bindgen 为 C 库创建 Rust FFI 绑定有两种方式:使用  命令行和使用 。本文我们使用这种方式作为示例进行说明。

1. 设置 crate 项目

一般 Rust FFI 绑定的 crate 项目会包含构建和导出 C 库的 unsafe 函数, crate 的 Rust 标准命名约定为,我们本次示例,针对 C 实现的库生成 Rust FFI 绑定。

首先是设置,添加作为构建时的依赖项,如下所示:

[build-dependencies]

bindgen="0.55.1"

在文件的部分,这样就声明了对 的构建时依赖并使用了最新版本 v0.55.1,可随时通过 crates.io bindgen 页面获取最新的版本信息。

其次在 crate 项目的根目录下创建一个文件,用来编译和链接的导出。我们可以通过 C 库的源代码,也可以直接通过链接库,本文选择通过链接库的方式。创建  文件内容如下:

#include

创建 文件内容如下:

fnmain() {

println!("cargo:rustc-link-lib=secp256k1");

println!("cargo:rerun-if-changed=wrapper.h");

letbindings = bindgen::Builder::default()

.header("wrapper.h")

.parse_callbacks(Box::new(bindgen::CargoCallbacks))

.generate()

.expect("Unable to generate bindings");

letout_path = PathBuf::from(env::var("OUT_DIR").unwrap());

bindings

.write_to_file(out_path.join("bindings.rs"))

.expect("Couldn't write bindings!");

}

其中:用来指定 C 库,传递给 cargo 告知 Rust 编译器 rustc 链接 secp256k1 共享库,可选的  可以是 ,,默认值是动态库 dylib,有关更多详细信息,请参见 。

是的主要入口点,可让为生成的绑定配置各种选项。用来指定要生成绑定的头文件。是指当更改包含的任何头文件时,生成的 crate 无效。

可以通过将绑定写入指定的文件,比如:。

2. 生成绑定

现在直接运行,将立即生成与的 Rust FFI 绑定。生成的绑定文件位于,其中由 cargo 根据 build.rs 确定,默认类似于。

中有如下内容:

#[repr(C)]

#[derive(Debug, Copy, Clone)]

pubstructsecp256k1_context_struct{

_unused: [u8;],

}

pubtypesecp256k1_context= secp256k1_context_struct;

#[repr(C)]

#[derive(Copy, Clone)]

pubstructsecp256k1_pubkey{

pubdata: [::std::os::raw::c_uchar;64usize],

}

由于 Rust 与 C 不同,不允许对结构体进行单独的声明和定义。我们可以看到用了一个私有的大小为零的类型字段,这是其默认执行的操作。

同时,会将 C 中的指针转换为Rust 中的 ,并将没有修饰符的 C 指针转换为。如下所示:

extern"C"{

pubfnsecp256k1_context_create(flags: ::std::os::raw::c_uint) -> *mutsecp256k1_context;

}

extern"C"{

pubfnsecp256k1_ec_pubkey_create(

ctx: *constsecp256k1_context,

pubkey: *mutsecp256k1_pubkey,

seckey: *const::std::os::raw::c_uchar,

) -> ::std::os::raw::c_int;

}

3. 使用生成的绑定,测试

我们可以使用 宏将生成的绑定直接转储到 crate 项目的入口中:

include!(concat!(env!("OUT_DIR"),"/bindings.rs"));

然后,我们可以编写测试,以验证生成的 Rust FFI 是否可以正常工作:

#[test]

fntest_create_pubkey() {

// secp256k1返回公钥

letmutpubkey: secp256k1_pubkey = secp256k1_pubkey {

data: [;64],

};

letprikey:u8=1;

unsafe{

letcontext = secp256k1_context_create(SECP256K1_CONTEXT_SIGN);

assert!(!context.is_null());

letret = secp256k1_ec_pubkey_create(& *context, &mutpubkey, &prikey);

assert_eq!(ret,1);

}

}

完整代码:https://github.com/lesterli/rust-practice/tree/master/ffi/secp256k1-sys

自定义生成的绑定

如果生成的绑定,我们可以通过以下几种方式对结构体,枚举等进行调整:

使用时,通过的配置方法。

使用命令行时,通过使用其它命令行选项。

也可以直接在C/C++源代码中添加注释。

具体可以参考:https://rust-lang.github.io/rust-bindgen/

与此同时,直接使用生成的 Rust FFI 绑定函数,需要通过  的方式访问 C 库中的函数,这不符合人体工程学,实际项目中,我们通常会提供一个安全的包装库。就是这样的一个包装 crate,它为的所有函数提供类型安全的 Rust 绑定,Github链接:https://github.com/rust-bitcoin/rust-secp256k1。

  • 发表于:
  • 原文链接https://kuaibao.qq.com/s/20200924A0IG5I00?refer=cp_1026
  • 腾讯「腾讯云开发者社区」是腾讯内容开放平台帐号(企鹅号)传播渠道之一,根据《腾讯内容开放平台服务协议》转载发布内容。
  • 如有侵权,请联系 cloudcommunity@tencent.com 删除。

扫码

添加站长 进交流群

领取专属 10元无门槛券

私享最新 技术干货

扫码加入开发者社群
领券