如何从Rust调用C ++动态库?
我想从Rust调用一个C ++动态库(* .so),但是我不想从Rust构建它.像这样
I want to call a C++ dynamic library (*.so) from Rust, but I don't want to build it from Rust. Like this,
cc::Build::new()
.file("src/foo.cc")
.shared_flag(true)
.compile("libfoo.so");
在某些情况下,我只需要调用几个函数,而不必调用所有函数.我该怎么用?
In some cases, I only need to call several functions, not all the functions. How can I use it?
Before you go further, make sure you have a basic idea of Rust FFI (foreign function interface).
在Rust中,调用C很容易,但是很难调用C ++.
In Rust, it's easy to call C, but hard to call C++.
要在Rust中调用C函数,只需用 extern
包装它们,进行一些基本的类型转换,有时甚至是 unsafe
.
To call C functions in Rust, you just have to wrap them with extern
, do some basic type casting and sometimes unsafe
.
要调用C ++函数,由于Rust不具备C ++功能的内在知识,因此您可能必须做很多手动翻译.例如,这是 Rust-Qt的文档的一部分:
To call C++ functions, since Rust does not have built-in knowledge of C++ features, you may have to do a lot of manual translation. For example, here is part of the documentation from Rust-Qt:
许多东西直接从C ++转换为Rust:
Many things are directly translated from C++ to Rust:
- 原始类型被映射到Rust的原始类型(例如
bool
)和libc crate提供的类型(例如libc :: c_int
). - 固定大小的数字类型(例如
int8_t
或qint8
)映射到Rust的固定大小类型(例如i8
). - 指针,引用和值映射到Rust的相应类型.
- C ++名称空间已映射到Rust子模块.
- C ++类和结构映射到Rust结构.这也适用于在库的API,包括依赖项的模板类.
- *功能映射到*功能.
- 将类方法映射到结构的实现.
- 析构函数映射到
Drop
和CppDeletable
实现. - 函数指针类型映射到Rust的等效表示形式.具有引用或类值的函数指针为不支持. 可以通过相应的特性在Rust中使用
-
static_cast
和dynamic_cast
.
- Primitive types are mapped to Rust's primitive types (like
bool
) and types provided by libc crate (likelibc::c_int
). - Fixed-size numeric types (e.g
int8_t
orqint8
) are mapped to Rust's fixed size types (e.g.i8
). - Pointers, references and values are mapped to Rust's respective types.
- C++ namespaces are mapped to Rust submodules.
- C++ classes and structs are mapped to Rust structs. This also applies to all instantiations of template classes encountered in the library's API, including template classes of dependencies.
- Free functions are mapped to free functions.
- Class methods are mapped to structs' implementations.
- Destructors are mapped to
Drop
andCppDeletable
implementations. - Function pointer types are mapped to Rust's equivalent representation. Function pointers with references or class values are not supported.
-
static_cast
anddynamic_cast
are available in Rust through corresponding traits.
Rust标识符的名称根据Rust的命名进行了修改惯例.
Names of Rust identifiers are modified according to Rust's naming conventions.
当无法直接翻译时:
- C ++库的每个包含文件的内容都放置在单独的子模块中.
- 通过在元组中包装参数并创建描述每个方法可接受的元组的特征来模拟方法重载.带有默认参数的方法将以相同的方式处理.
- 将单个继承转换为
Deref
和DerefMut
实现,从而允许在派生类上调用基类方法对象.当deref强制不够时,应使用static_cast
用于从派生类转换为基类. - 为每个公共类字段创建etter和setter方法.
- Contents of each include file of the C++ library are placed into a separate submodule.
- Method overloading is emulated with wrapping arguments in a tuple and creating a trait describing tuples acceptable by each method. Methods with default arguments are treated in the same way.
- Single inheritance is translated to
Deref
andDerefMut
implementation, allowing to call base class methods on derived objects. When deref coercions are not enough,static_cast
should be used to convert from derived to base class. - Getter and setter methods are created for each public class field.
尚未实施,但已计划
- 将C ++
typedef
转换为Rust类型别名. - 基于C ++运算符方法( issue )对结构执行运算符特征.运营商当前以带有
op _
前缀的常规函数公开. - 如果在C ++方面存在适用的方法,则对结构实施调试和显示特征.
- 为集合实现迭代器特征.
- 子类化API(问题).
- 提供对类的公共变量的访问权限(问题).
- 提供从枚举到int的转换(在Qt API中使用).
- 支持嵌套在模板类型中的C ++类型,例如
Class1< T> :: Class2
.
- Translate C++
typedef
s to Rust type aliases. - Implement operator traits for structs based on C++ operator methods (issue). Operators
are currently exposed as regular functions with
op_
prefix. - Implement Debug and Display traits for structs if applicable methods exist on C++ side.
- Implement iterator traits for collections.
- Subclassing API (issue).
- Provide access to a class's public variables (issue).
- Provide conversion from enums to int and back (used in Qt API).
- Support C++ types nested into template types, like
Class1<T>::Class2
.
不打算支持:
- 高级模板用法,例如带有整数模板参数的类型.
- 模板的部分专业化.
- 模板方法和函数.
我的建议是将C ++库包装为C库,然后将其称为正式FFI方式,或使用 rust-bindgen 自动进行包装.
My suggestion is to wrap your C++ library as a C library, then call it the official FFI way, or use rust-bindgen to automatically do the wrapping.
如果您仍然想在Rust中调用C ++,那么 rustcxx 似乎是一个方便的工具.
If you still want to call C++ in Rust, rustcxx seems like a handy tool.
关于库链接,这很简单:
As to the library linking, it's pretty simple:
- 将库放入系统库中,以搜索
/usr/lib
或/usr/local/lib/
之类的路径,确保可以通过找到它ldconfig -p
. - 或使用环境变量
LD_LIBRARY_PATH
指定从CLI运行cargo
时库所在的路径.
- Put the library into your system library searching paths like
/usr/lib
or/usr/local/lib/
, make sure it can be found byldconfig -p
. - Or use the environment variable
LD_LIBRARY_PATH
to specify the path where your library lays when you runcargo
from the CLI.