3个版本 (破坏性更新)
0.3.0 | 2021年10月25日 |
---|---|
0.2.0 | 2020年4月7日 |
0.1.0 | 2018年4月21日 |
#371 in Rust模式
454,936 每月下载量
在 2 crate 中使用
48KB
646 行
FFI Helper
一个使FFI代码更易于使用的crate。
这是我们工作中使用的工具crate的开源版本。最初目的是让Rust模块(DLL)更容易与我们的主GUI应用程序集成。我们发现它使用起来特别优雅和稳健,所以认为与全世界分享是一件好事。
特性
这试图为你提供一套抽象,可以在此基础上构建安全的API。它试图解决编写FFI代码时遇到的一些常见问题。
错误处理
错误处理通过一个私有的线程局部变量 LAST_ERROR
完成,它允许你使用类似于 errno
的机制来指示错误。
如果Rust函数返回一个 Result::Err(_)
,它将错误传递给 LAST_ERROR
并返回一个 明显错误 的值(例如,null
或 0
)。然后调用者检查这个返回值,并可以检查 LAST_ERROR
以获取更多信息。
提供了一个宏,允许你从C中检查 LAST_ERROR
。
空指针
null_pointer_check!()
宏将检查某些 可空 的东西是否为空,如果是,它将使用错误返回值(对于返回指针的函数是 null
,对于整数是 0
)并设置 LAST_ERROR
来指示遇到了空指针。
我们使用一个 Nullable
trait 来表示具有某种 "明显无效" 值的任何东西(例如,null 指针,
0
)。
pub trait Nullable {
const NULL: Self;
fn is_null(&self) -> bool;
}
null_pointer_check!()
允许您检查某个特定的事物是否无效,设置 LAST_ERROR
,并使用 Nullable::NULL
早期从当前函数返回。
实际上,这使得处理无效输入的可能性变得相当方便。
struct Foo {
data: Vec<u8>,
}
#[no_mangle]
unsafe extern "C" fn foo_get_data(foo: *const Foo) -> *const u8 {
null_pointer_check!(foo);
let foo = &*foo;
foo.data.as_ptr()
}
异常安全性
当一段Rust代码崩溃并尝试跨越FFI边界回溯时,异常安全性成为一个问题。目前这会导致程序中止,虽然不再是直接的 未定义行为,但这仍然是一个非常麻烦的问题。
存在一个 catch_panic()
函数,它允许您执行一些代码并将捕获任何回溯,适当更新 LAST_ERROR
。宏 catch_panic!()
使这变得更容易一些,并且与 Nullable
特性一起工作,因此您可以从函数中退出,返回错误(Nullable::NULL
)。
将闭包拆分为数据和代码
对于与回调一起工作的FFI函数来说,接受一个额外的 void *user_data
参数,该参数指向用户可能想要使用的任何额外状态,这是相当常见的。这并不允许程序员使用
可以使用 split_closure()
函数将闭包指针“拆分”为其数据的指针和一个 unsafe extern "C" fn()
,它可以用作回调。
Rust闭包通过生成一个自定义类型来包含任何捕获的状态,并为 FnMut()
(或 Fn()
,或 FnOnce()
)实现。此函数通过将闭包指针强制转换为 void *
(这是我们的数据)来工作(这是我们的数据),并定义一个trampoline函数,该函数将数据转换回并调用闭包。
这实际上是这个的泛化
fn split<C>(closure: &mut C) -> (*mut c_void, unsafe extern "C" fn(*mut c_void))
where C: FnMut()
{
unsafe extern "C" fn trampoline<T>(user_data: *mut c_void) {
let closure: &mut T = &mut *(user_data as *mut T);
closure();
}
(closure as *mut C as *mut c_void, trampoline::<T>)
}
异步任务
任务API 帮助处理在后台线程上运行作业时遇到的复杂并发问题,同时保持内存和线程安全。
Task
特性本身非常简单
pub trait Task: Send + Sync + Clone {
type Output: Send + Sync;
fn run(&self, cancel_tok: &CancellationToken) -> Result<Self::Output, Error>;
}
然后您可以通过 export_task!()
宏生成绑定。这将声明各种 extern "C"
函数,用于在后台线程上创建 Task
,定期检查是否完成,允许您取消任务,然后检索结果并在之后适当地清理一切。
这可能是该库的 杀手特性,因为它允许您轻松地在后台运行Rust任务,允许您将其集成到更大的应用程序/GUI中。
强烈建议访问 task
模块的文档,以获取更详细的说明。
依赖项
~0.4–1MB
~21K SLoC