From ad263b49e2e8102bc1f012314c8468554443eb5c Mon Sep 17 00:00:00 2001 From: fangting Date: Mon, 24 Apr 2023 20:07:27 +0800 Subject: [PATCH] Add README_zh.md Issue:#I6YAO1 Signed-off-by: fangting --- README_zh.md | 154 +++++++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 154 insertions(+) create mode 100644 README_zh.md diff --git a/README_zh.md b/README_zh.md new file mode 100644 index 00000000..482e81fa --- /dev/null +++ b/README_zh.md @@ -0,0 +1,154 @@ +CXX — Rust和C++之间的安全FFI +========================================= + +[github](https://github.com/dtolnay/CXX) +[crates.io](https://crates.io/crates/CXX) +[docs.rs](https://docs.rs/cxx) +[build status](https://github.com/dtolnay/CXX) + + +## 概述 + + +CXX工具提供了一种安全的互相调用机制,可以实现rust和C++的互相调用,而不会像使用bindgen或cbindgen那样,生成不安全的C风格的绑定时,可能会出现很多问题。但是,这并不能改变C++代码100%是不安全的事实。当检查一个项目的安全性时,需要负责审核所有不安全的Rust代码和所有的C++代码。这种安全理念下的检查思想是,只对C++端进行检查,就足以发现所有不安全问题,也就是说,Rust方面被认为是100%安全的。 + +CXX通过FFI(Foreign Function Interface)和函数签名的形式来实现接口和类型声明,并对类型和函数签名进行静态分析,以维护Rust和C++的不变量和要求。 + +如果所有的静态分析都通过了,那么CXX就会使用一对代码生成器来生成两侧相关的`extern "C"`签名,以及任何必要的静态断言,以便在以后的构建过程中验证正确性。在Rust侧,这个代码生成器只是一个属性宏。在C++方面,它可以是一个小型的Cargo构建脚本或者别的构建系统,如Bazel或Buck,CXX也提供了一个命令行工具来生成头文件和源文件,容易集成。 + +产生的FFI桥的运行成本为零或可忽略不计,也就是说,没有复制,没有序列化,没有内存分配,也不需要运行时检查。 + +FFI的签名能够使用来自任何一方的原生类型、比如Rust的`String`或C++的`std::string`,Rust的`Box`或C++的`std::unique_ptr`,Rust的`Vec`或C++的`std::vector`,等等的任意组合。CXX保证ABI兼容性,基于关键的标准库类型的内置绑定,在这些类型上向另一种语言暴露API。这些类型有着明确的对应关系,例如,当从Rust操作一个C++字符串时的时候,它的`len()`方法就变成了对C++定义的`size()`成员函数的调用。当从C++操作一个Rust字符串时,其`size()`成员函数函数调用Rust的`len()`。 + +
+ +## 使用指导 + +建议初学者参考CXX官网指导 ****,了解具体CXX的使用方法,然后在build仓的rust/test目录下具体使用CXX来实现接口转换。其中test_cxx介绍了C++调用rust的示例,test_cxx_rust介绍了rust调用C++的示例。 + +
+ + +## 细节 + +FFI边界的语言涉及3种类型的字段: + +- **共享结构** +该字段对两种语言都是可见的。 + +- **不透明类型** +该字段对另一种语言是保密的。这些类型不能通过FFI的值来传递,而只能在间接传递,比如一个引用`&`,一个Rust`Box`,或者一个`UniquePtr`。可以是一个类型别名可以是一个类型别名,用于任意复杂的通用语言特定类型,这取决于自己写的用例。 + +- **函数** +在任一语言中实现,可从另一语言中调用。 + +
+ +## 与bindgen的对比 + +bindgen主要用来实现rust代码对c接口的单向调用;CXX工具可以实现rust和C++的互相调用。 + +
+ +## 基于cargo的构建 + +对于由Cargo的构建,需要使用一个构建脚本来运行CXX的C++代码生成器。 + +典型的构建脚本如下: + +[`cc::Build`]: https://docs.rs/cc/1.0/cc/struct.Build.html + +```toml +# Cargo.toml + +[build-dependencies] +CXX-build = "1.0" +``` + +```rust +// build.rs + +fn main() { + CXX_build::bridge("src/main.rs") // returns a cc::Build + .file("src/demo.cc") + .flag_if_supported("-std=C++11") + .compile("cxxbridge-demo"); + + println!("cargo:rerun-if-changed=src/main.rs"); + println!("cargo:rerun-if-changed=src/demo.cc"); + println!("cargo:rerun-if-changed=include/demo.h"); +} +``` + +
+ +## 基于非cargo的构建 + +对于在非Cargo构建中的使用,如Bazel或Buck,CXX提供了另一种方式产生C++侧的头文件和源代码文件,作为一个独立的命令行工具使用。 + +```bash +$ cargo install cxxbridge-cmd +$ cxxbridge src/main.rs --header > path/to/mybridge.h +$ cxxbridge src/main.rs > path/to/mybridge.cc +``` + +
+ +## 安全性 + +确保安全性需要考虑以下内容: + +- 设计让配对代码生成器一起工作,控制FFI边界的两边。通常情况下,在Rust中编写自己的`extern "C"`块是是不安全的,因为Rust编译器没有办法知道每个开发者的签名是否真的与别的语言实现的签名相匹配。有了CXX,就可以实现这种可见性,并且知道另一边是什么的内容。 + +- 静态分析可以检测并防止不应该以值传递的类型从C++到Rust中以值传递的类型,例如,因为它们可能包含内部指针,而这些指针会被Rust的移动行为所破坏。 + +- 令人惊讶的是,Rust中的结构和C++中的结构的布局/字段/对齐方式/一切都完全相同、但在通过值传递时,仍然不是相同的ABI。这是一个长期存在的bindgen的错误,导致看起来正确的代码出现segfaults([issue_778](https://github.com/rust-lang/rust-bindgen/issues/778))。CXX知道这一点,并且可以插入必要的零成本的解决方法,所以请继续并毫无顾虑地传递开发者写的结构。这可以通过拥有边界的两边,而不是只有一边。 + +- 模板实例化:例如,为了在Rust中展示一个`UniquePtr`类型,该类型由一个真正的C++的`unique_ptr`支持。为了在Rust中展示一个由真正的C++`unique_ptr`的指针类型,可以使用Rust trait来将行为连接到由别的语言执行的模板实例上。 + + +
+ +## 内置类型 + +除了所有的原生类型(i32 <=> int32_t)之外,还有以下常见类型可用于共享结构的字段以及函数的参数和返回值。 + + + + + + + + + + + + + + + + + +
name in Rustname in C++restrictions
Stringrust::String
&strrust::Str
&[T]rust::Slice<const T>cannot hold opaque C++ type
&mut [T]rust::Slice<T>cannot hold opaque C++ type
CXXStringstd::stringcannot be passed by value
Box<T>rust::Box<T>cannot hold opaque C++ type
UniquePtr<T>std::unique_ptr<T>cannot hold opaque Rust type
SharedPtr<T>std::shared_ptr<T>cannot hold opaque Rust type
[T; N]std::array<T, N>cannot hold opaque C++ type
Vec<T>rust::Vec<T>cannot hold opaque C++ type
CXXVector<T>std::vector<T>cannot be passed by value, cannot hold opaque Rust type
*mut T, *const TT*, const T*fn with a raw pointer argument must be declared unsafe to call
fn(T, U) -> Vrust::Fn<V(T, U)>only passing from Rust to C++ is implemented so far
Result<T>throw/catchallowed as return type only
+ +`rust`命名空间的C++ API是由*include/CXX.h*文件定义的。使用这些类型时种类的时候,需要C++代码中包含这个头文件。 + +以下类型很快被支持,只是还没有实现。 + + + + + + + + + +
name in Rustname in C++
BTreeMap<K, V>tbd
HashMap<K, V>tbd
Arc<T>tbd
Option<T>tbd
tbdstd::map<K, V>
tbdstd::unordered_map<K, V>
+ +
+ +## 剩余工作 + +当前CXX工具还没有达到普遍使用阶段,在使用该工具的过程中有任何问题欢迎开发者在社区反馈。 + +