且构网

分享程序员开发的那些事...
且构网 - 分享程序员编程开发的那些事

包装 C lib 初始化/销毁例程的推荐方法

更新时间:2023-11-25 11:09:46

我不知道除了措辞强硬的文档之外,没有办法强制在主线程中调用方法.因此,忽略该要求... :-)

I know of no way of enforcing that a method be called in the main thread beyond strongly-worded documentation. So, ignoring that requirement... :-)

通常,我会使用 std::sync::Once,这似乎基本上是为这种情况设计的:

Generally, I'd use std::sync::Once, which seems basically designed for this case:

一种同步原语,可用于运行一次性全局初始化.用于 FFI 或相关的一次性初始化功能.这种类型只能用 ONCE_INIT 构造价值.

A synchronization primitive which can be used to run a one-time global initialization. Useful for one-time initialization for FFI or related functionality. This type can only be constructed with the ONCE_INIT value.

请注意,没有任何清理的规定;很多时候你只需要泄漏图书馆所做的一切.通常,如果一个库有一个专用的清理路径,它也被构造为将所有初始化数据存储在一个类型中,然后作为某种上下文或环境传递到后续函数中.这将很好地映射到 Rust 类型.

Note that there's no provision for any cleanup; many times you just have to leak whatever the library has done. Usually if a library has a dedicated cleanup path, it has also been structured to store all that initialized data in a type that is then passed into subsequent functions as some kind of context or environment. This would map nicely to Rust types.

警告

您当前的代码没有像您希望的那样具有保护性.由于您的 App 是一个空结构,最终用户可以在不调用您的方法的情况下构建它:

Your current code is not as protective as you hope it is. Since your App is an empty struct, an end-user can construct it without calling your method:

let _init_ = App;

我们将使用零大小的参数来防止这种情况.另请参阅定义指向 C 不透明指针的字段的 Rust 习语是什么? 了解为以下对象构造不透明类型的正确方法外国金融机构.

We will use a zero-sized argument to prevent this. See also What's the Rust idiom to define a field pointing to a C opaque pointer? for the proper way to construct opaque types for FFI.

总而言之,我会使用这样的东西:

Altogether, I'd use something like this:

use std::sync::Once;

mod ffi {
    extern "C" {
        pub fn InitializeMyCLib();
        pub fn CoolMethod(arg: u8);
    }
}

static C_LIB_INITIALIZED: Once = Once::new();

#[derive(Copy, Clone)]
struct TheLibrary(());

impl TheLibrary {
    fn new() -> Self {
        C_LIB_INITIALIZED.call_once(|| unsafe {
            ffi::InitializeMyCLib();
        });
        TheLibrary(())
    }

    fn cool_method(&self, arg: u8) {
        unsafe { ffi::CoolMethod(arg) }
    }
}

fn main() {
    let lib = TheLibrary::new();
    lib.cool_method(42);
}