#visual-studio #cpp

directcpp

从 Rust 调用 C++

10 个版本

0.1.9 2024年8月6日
0.1.8 2024年8月6日
0.1.7 2024年7月3日
0.1.6 2024年6月21日
0.1.4 2024年4月29日

#89 in FFI

Download history 268/week @ 2024-04-24 30/week @ 2024-05-01 254/week @ 2024-06-19 8/week @ 2024-06-26 130/week @ 2024-07-03 9/week @ 2024-07-24 172/week @ 2024-07-31 85/week @ 2024-08-07

每月266次下载

MIT/Apache

33KB
732

从 Rust 调用 C++!

directcpp 是另一种与 Rust 互操作 C++ 的方法。

它设计得轻量级、高效、MSVC 友好,无需在 C++ 端生成代码,可以与像 Visual Studio 这样的外部 C++ 构建工具一起工作。

如果您正在寻找一种使用 Visual Studio 来与 C++ 和 Rust 一起工作的方式,directcpp 可能是结果。

为什么不使用 crate cxx

Crate cxx 是一个用于 C++/Rust 互操作的有力 crate。然而,在我的环境中,Visual Studio 非常重要,我需要它来调试我的 C++ 代码。对于 MSVC,crate cxx 并不够友好。它有预编译的 C++ 代码,并且无法在调试配置下编译。具体来说,这是因为使用迭代调试器编译的代码与未使用它的代码不能一起工作。

同时,我不想让代码生成器生成所有交互所需的代码。这似乎在 C++ 和 Rust 之间创建了一个自然的障碍。我希望 Rust 可以像普通库一样使用 C++ 代码。当然,这并不是没有代价的,我会尽量少用这些接口。因此,我希望需要使用的接口可以任意选择,而无需严格遵循某种范式。

当然,安全性仍然非常重要,我不想在 C++ 端做出太多妥协并为 Rust 创建很多奇怪的函数。Rust 端也是如此。不要将一些结构转换为 C++ 可以识别的形式来调用 C++ -- 尽管我一开始就是这样做的 -- 因为这看起来相当笨拙。一些糟糕的代码占用了我的文本的大量空间,使我难以阅读自己的代码。

因此,我选择生成少数几个非常自由的接口,以便 Rust 可以以 99% 的安全性自由调用 C++ 代码。我不敢说它是 100%,尽管 crate cxx 可以做到这一点。毕竟,当您从 Rust 调用 C++ 时,您可以缓存从 C++ 获得的任何指针,这是您的自由,但也是您为自由付出的代价。

入门

如果您想开始使用 directcpp,可以从阅读其测试代码开始。这些代码位于github的 test_proj 目录中,我在那里展示了如何让visual studio和rust和平共存,并支持调试版本编译。我使用黑客方法允许rust链接到 msvcrtd 调试库。

您可以先尝试最简单的版本。

use directcpp;

#[directcpp::bridge]
extern "C++" {
	pub fn on_start();
}

然后在您的cpp文件中,编写

void on_start(void)
{
	std::cout << "on cpp side start!" << std::endl;
}

注意 extern "C"不需要 的。Rust会直接调用C++函数的混淆版本。这就是为什么它被称为 directcpp

您还可以将结构体的引用传递到C++中,并返回另一个结构体。

#[directcpp::bridge]
extern "C++" {
	pub fn on_magic(magic: &MagicIn) -> MagicOut;
}

要这样做,MagicInMagicOut 结构体在C++和Rust之间应具有完全相同的内存布局。我在github中提供了 rust_spt.h,您可以自由地包含它。这是不生成C++端代码的代价,同时也赋予了编写和编译C++代码的自由。

您还可以从C++端获取类对象指针,并通过Rust中的 SharedPtr 或 C++中的 std::shared_ptr 调用其成员函数,同时保持安全性。请参阅 test_proj 中的代码。

尽管如此,您可能需要根据您的方式来生成一些代码,因为我使用了 rust2h.py 来从Rust生成一些具有相同内存布局的C++结构,如 MagicInMagicOutrust2h.py 不应被视为 directcpp 的一部分,但它与其他东西一起工作,共同生成最终产品。因此,directcpp 是一个松散组织的库,提供了最大的自由度,但您可能需要一点时间来习惯它。

我已经将 rust2h.py 放入了工具文件夹。然而,转换这样的结构并不困难,您甚至可以使用像 sed 这样的文本操作工具来完成。

特殊映射

通常,directcpp 将参数中的名称映射为与C++中同名的类型。但是,有一些特殊类型

  • &CStr 映射到:C++中的 const char*
  • &str 映射到 C++中的 const char*, size_t
  • &[u8] 映射到 C++中的 const uint8_t*, size_t
  • String 映射到 RustString

测试平台

  • Windows with MSVC, x86_64
  • Linux, x86_64
  • MacOS, x86_64
  • MacOS, aarch64

依赖项

~2.4–4MB
~74K SLoC