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

Rust FFI 编程-cbindgen 工具介绍

cbindgen 是一个从 Rust 库(这个库已面向暴露 C 接口进行设计)生成 C/C++ 头文件的工具。

我们在最初 Rust 生态还没起来的时候,一般都是使用 Rust 对已有的 C 库进行封装,这时,就会用到 bindgen 多一些。但是随着 Rust 生态越来越成熟,可能大量的库直接使用 Rust 实现了。这时,反而想导出 C 接口,进而供其它语言调用,这时,就会用到 cbindgen 了。

为什么这类工作,需要用到这种辅助工具呢?因为,真的很枯燥啊!!!

其实,FFI封装、转换,熟悉了之后,知识点就那些,模式也比较固定,如果接口量很大,那就需要大量重复的 coding。量一大,人手动绑定出错的机率也大。所以这种辅助工具的意义就显露出来了。基于辅助工具生成的代码,如不完美,再适当手动修一修,几下就能搞定,大大提高生产效率。

当然,如果你的 Rust 库,只是导出一两个接口,那就没必要使用这个工具了。

如何使用 cbindgen

使用 cbindgen 有两种方式:

以命令行工具的方式使用;

以库的方式在 build.rs 中使用;

两种方式各有其方便和不方便之处。第一种形式不需要写代码,但是每次 Rust 库修改升级后,可能要重新运行一次这个命令行,以生成最新的 C 头文件。第二种形式在 Rust 库编译的过程中,就自动生成了 C 头文件。

下面我们来看第一种方式。

命令行工具方式

安装 cbindgen

cargoinstall--force cbindgen

对一个暴露了 C API 的 Rust crate,直接:

cbindgen--configcbindgen.toml--cratemy_rust_library--outputmy_header.h

就可以了。 就是生成的头文件。

要注意两点:

不是任意 Rust crate 都可以,而是已经做了暴露 C API 开发的库才行。因为 cbindgen 会去扫描整个源代码,把对接的接口抽出来;

可以通过 cbindgen.toml 这个配置文件,给 cbindgen 配置行为参数,参数很多,后面有参考链接。

build.rs 方式

build.rs 的基础知识,不在这里讲了,传送门在这里:https://doc.rust-lang.org/cargo/reference/build-scripts.html。

build.rs 的功能就是在编译期间,在编译真正的 crate 之前,先编译执行 build.rs 中的代码。于是,可以在这里面做 on-fly 生成之类的工作。

按照下面这种模板使用 cbindgen 就好了:

externcratecbindgen;

usestd::env;

fnmain() {

letcrate_dir = env::var("CARGO_MANIFEST_DIR").unwrap();

cbindgen::Builder::new()

.with_crate(crate_dir)

.generate()

.expect("Unable to generate bindings")

.write_to_file("bindings.h");

}

在 build.rs 方式里,也是可以配置 cbindgen.toml 参数的。这里,编译之后,生成的  就是我们要的 C 头文件。

生成的结果看起来是什么样子?

比如,我们有一个 Rust crate:

// trebuchet.rs

#[repr(u8)]

enumAmmo{

Rock,

WaterBalloon,

Cow,

}

#[repr(C)]

structTarget{

latitude:f64,

longitude:f64,

}

// notice: #[repr(rust)]

structTrebuchet{ ... }

#[no_mangle]

unsafeextern"C"fntrebuchet_new() -> *mutTrebuchet { ... }

#[no_mangle]

unsafeextern"C"fntrebuchet_delete(treb: *mutTrebuchet) { ... }

#[no_mangle]

unsafeextern"C"fntrebuchet_fire(treb: *mutTrebuchet,

ammo: Ammo,

target: Target) { ... }

生成后的 C header 文件如下:

// trebuchet.h

#include

#include

extern"C"{

enumclassAmmo:uint8_t{

Rock =,

WaterBalloon =1,

Cow =2,

};

structTrebuchet;

structTarget{

doublelatitude;

doublelongitude;

};

voidtrebuchet_delete(Trebuchet *treb);

voidtrebuchet_fire(Trebuchet *treb, Ammo ammo, Target target);

Trebuchet*trebuchet_new();

}// extern "C"

可以看到,cbindgen 完整实现了我们的意图:

Ammo 有正确的大小和值;

Target 包含所有字段和正确的布局(字段顺序);

Trebuchet 被声明为一个 opaque 结构体;

所有的函数有了对应的声明。

补充

cbindgen 不但可以生成 C 头文件,还可以生成 C++ 的,甚至还可以生成 swift 可以用的头文件;

cbindgen 不是一包到底的,对 C 的支持相对成熟,对 C++ 的差一些。适当的时候,还是需要手动做很多事情的;

cbindgen 对 Rust 的泛型,有一定的支持;

在下一篇,我们将会使用 cbindgen 对我们之前写的库做一下生成实验

参考链接

说明文档

cbindgen.toml

announcing-cbindgen

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

扫码

添加站长 进交流群

领取专属 10元无门槛券

私享最新 技术干货

扫码加入开发者社群
领券