2个版本
0.1.1 | 2022年9月21日 |
---|---|
0.1.0 | 2022年9月3日 |
#842 在 异步
54KB
839 行
cxx-async
概述
cxx-async
是一个Rust包,它扩展了 cxx
库,以便通过 async/
await
使用 co_await
在异步Rust代码和 C++20 协程 之间提供互操作性。如果你的C++代码是异步的,cxx-async
可以提供一个更方便的,也许更高效的,相对于回调的替代方案。你可以自由地在C++协程和Rust futures和/或 streams 之间转换,并从另一个中等待。
重要的是要强调 cxx-async
不是什么:它不是Tokio或其他Rust I/O库的C++绑定。它也不是Rust绑定到 boost::asio
或类似库。这样的绑定如果需要可以在 cxx-async
之上进行分层,但这个包并不提供它们。请注意,即使在理论中,这也是一个棘手的问题,因为Rust异步I/O代码通常紧密耦合到单个库(如Tokio),与C++异步I/O代码紧密耦合到像 boost::asio
这样的库一样。如果你在编写服务器代码,你仍然可以使用 cxx-async
,但你需要确保Rust和C++两边运行独立的I/O执行器。
cxx-async
旨在与流行的C++协程支持库兼容。目前,轻量级的 cppcoro
和更全面的 Folly 都得到了支持。欢迎提交拉取请求以支持其他库。
快速教程
要使用 cxx-async
,首先将 cxx
添加到你的项目中。然后,将以下内容添加到你的 Cargo.toml
[dependencies]
cxx-async = "0.1"
现在,在 #[cxx::bridge]
模块中,声明一个future类型和一些方法,如下所示
#[cxx::bridge]
mod ffi {
// Declare type aliases for each of the future types you wish to use here. Then declare
// async C++ methods that you wish Rust to call. Make sure they return one of the future
// types you declared.
unsafe extern "C++" {
type RustFutureString = crate::RustFutureString;
fn hello_from_cpp() -> RustFutureString;
}
// Async Rust methods that you wish C++ to call go here. Again, make sure they return one of the
// boxed future types you declared above.
extern "Rust" {
fn hello_from_rust() -> Box<RustFutureString>;
}
}
在 #[cxx::bridge]
块之后,使用 #[cxx_async::bridge]
属性定义未来类型
// The inner type is the Rust type that this future yields.
#[cxx_async::bridge]
unsafe impl Future for RustFutureString {
type Output = String;
}
现在,在你的 C++ 文件中,确保包含正确的头文件
#include "rust/cxx.h"
#include "rust/cxx_async.h"
#include "rust/cxx_async_cppcoro.h" // Or cxx_async_folly.h, as appropriate.
并添加对 CXXASYNC_DEFINE_FUTURE
宏的调用,以定义未来 C++ 方面
// The first argument is the C++ type that the future yields, and the second argument is the
// fully-qualified name of the future, with `::` namespace separators replaced with commas. (For
// instance, if your future is named `mycompany::myproject::RustFutureString`, you might write
// `CXXASYNC_DEFINE_FUTURE(rust::String, mycompany, myproject, RustFutureString);`. The first
// argument is the C++ type that `cxx` maps your Rust type to: in this case, `String` maps to
// `rust::String`, so we supply `rust::String` here.
//
// This macro must be invoked at the top level, not in a namespace.
CXXASYNC_DEFINE_FUTURE(rust::String, RustFutureString);
一切准备就绪!现在你可以定义 Rust 可以调用的异步 C++ 代码
RustFutureString hello_from_cpp() {
co_return std::string("Hello world!");
}
在 Rust 方面
async fn call_cpp() -> String {
// This returns a Result (with the error variant populated if C++ threw an exception), so you
// need to unwrap it:
ffi::hello_from_cpp().await.unwrap()
}
同样,定义一些 C++ 可以调用的异步 Rust 代码
use cxx_async::CxxAsyncResult;
fn hello_from_rust() -> RustFutureString {
// You can instead use `fallible` if your async block returns a Result.
RustFutureString::infallible(async { "Hello world!".to_owned() })
}
在 C++ 方面
cppcoro::task<rust::String> call_rust() {
co_return hello_from_rust();
}
这就对了!你现在可以在任一侧自由地等待未来。可以遵循类似的程序将 C++ 协程(使用 co_yield
产生值)包装在 Rust 流中。
安装说明
您需要一个实现了协程 TS 的 C++ 编译器,这通常与对 C++20 的支持相一致。一些实现协程 TS 的 C++ 编译器(例如 Apple clang 13.0.0)在编译 Folly 时会崩溃。还建议使用 libc++
而不是 libstdc++
,因为前者对协程有更完整的支持。
使用 cxx-async
与 Folly 需要Folly具有协程支持。这通常意味着您需要使用 -DCXX_STD=20
构建Folly。许多 Folly 的分发(例如 Homebrew 中的分发)没有启用协程支持;这种错误的一个常见症状是链接错误,提到了缺少符号 folly::resumeCoroutineWithNewAsyncStackRoot
。
行为准则
cxx-async
遵循 Rust 本身的同一行为准则。可以向包作者提交报告。
许可协议
根据您的选择,受 Apache License 2.0 或 MIT 许可协议的许可。
除非您明确说明,否则根据 Apache-2.0 许可协议定义的,您有意提交的任何贡献,都将按照上述方式双许可,不附加任何其他条款或条件。
依赖关系
~1.3–3MB
~49K SLoC