如何从Rust调用C ++动态库?

Chi*_*hen 1 c++ ffi rust

我想从Rust调用一个C ++动态库(* .so),但是我不想从Rust构建它。像这样,

cc::Build::new()
    .file("src/foo.cc")
    .shared_flag(true)
    .compile("libfoo.so");
Run Code Online (Sandbox Code Playgroud)

在某些情况下,我只需要调用几个函数,而不是所有函数。如何使用?

Eri*_*rik 7

我觉得值得一提的是,现在 crate 中有 CXX 库

https://docs.rs/cxx/latest/cxx/

https://cxx.rs/

这就是我今后将使用的内容,以便与供应商的 C++ 库进行互操作。

分配的答案有一个链接,但它是公开存档的,所有活跃的工作都可能在这里:

https://github.com/dtolnay/cxx


Dan*_*man 5

根据Rust 的官方网站,没有官方支持与 C++ 的联动。相反,您可以尝试使用 C 库。

在他们的用户论坛中也有一个关于这个问题的帖子,用户在那里建议了一些旨在解决这个问题的 3rd 方项目:

  • bindgen - 为 Rust 自动生成 FFI
  • cpp-to-rust - 允许使用来自 Rust 的 C++ 库。这个项目的主要目标是 Qt。

我没有使用它们,所以我不能推荐任何特别的东西或分享我的经验,但祝你好运:)


Gal*_*axy 5

在继续之前,请确保您具有Rust FFI(外部功能接口)的基本概念。

在Rust中,调用C很容易,但是很难调用C ++。

要在Rust中调用C函数,只需将其包装起来extern,并进行一些基本的类型转换unsafe

要调用C ++函数,由于Rust没有内置的C ++功能知识,您可能需要做很多手动翻译。例如,这是Rust-Qt的文档的一部分:

许多事情直接从C ++转换为Rust:

  • 基本类型映射到Rust的基本类型(如bool)和libc crate提供的类型(如libc::c_int)。
  • 固定大小的数值类型(例如int8_tqint8)被映射到Rust的固定大小类型(例如i8)。
  • 指针,引用和值映射到Rust的相应类型。
  • C ++名称空间被映射到Rust子模块。
  • C ++类和结构映射到Rust结构。这也适用于库API中遇到的所有模板类实例化,包括依赖项的模板类。
  • 自由函数被映射到自由函数。
  • 类方法映射到结构的实现。
  • 析构函数映射到DropCppDeletable实现。
  • 函数指针类型映射到Rust的等效表示形式。不支持带有引用或类值的函数指针。
  • static_cast并且dynamic_cast可以通过相应的特性在Rust中使用。

Rust标识符的名称根据Rust的命名约定进行了修改。

如果无法直接翻译:

  • C ++库的每个包含文件的内容都放置在单独的子模块中。
  • 通过将参数包装在元组中并创建描述每个方法可接受的元组的特征来模拟方法重载。具有默认参数的方法以相同的方式处理。
  • 单继承被翻译成DerefDerefMut实现,允许呼吁派生的对象基类的方法。当deref强制不足时,static_cast应使用将其从派生转换为基类。
  • 将为每个公共类字段创建Getter和setter方法。

尚未实施,但已计划:

  • 将C ++转换typedef为Rust类型的别名。
  • 根据C ++运算符方法(issue)实现结构的运算符特征。当前,运算符公开为带有op_前缀的常规函数。
  • 如果C ++方面存在适用的方法,请为结构实现Debug和Display特性。
  • 实现集合的迭代器特征。
  • 子类化API(问题)。
  • 提供对类的公共变量(issue)的访问。
  • 提供从枚举到int的转换(在Qt API中使用)。
  • 支持嵌套在模板类型中的C ++类型,例如Class1<T>::Class2

不打算支持:

  • 高级模板用法,例如带有整数模板参数的类型。
  • 模板部分专业化。
  • 模板方法和功能。

我的建议是将C ++库包装为C库,然后将其称为正式FFI方式,或使用rust-bindgen自动进行包装。

如果您仍然想在Rust中调用C ++,则rustcxx似乎是一个方便的工具。

关于库链接,这很简单:

  • 将库放入系统库搜索路径,例如/usr/lib/usr/local/lib/,确保可以通过找到ldconfig -p
  • 或使用环境变量LD_LIBRARY_PATH来指定cargo从CLI 运行时库所在的路径。