#dlopen #dylib #api-bindings

dlib

处理手动加载可选系统库的辅助宏

11 个版本

使用旧的 Rust 2015

0.5.2 2023 年 6 月 5 日
0.5.0 2021 年 2 月 25 日
0.4.2 2020 年 5 月 28 日
0.4.1 2018 年 3 月 13 日
0.2.0 2015 年 11 月 9 日

#109 in Rust 模式

Download history 118666/week @ 2024-04-23 113494/week @ 2024-04-30 113038/week @ 2024-05-07 118123/week @ 2024-05-14 116293/week @ 2024-05-21 117869/week @ 2024-05-28 121584/week @ 2024-06-04 130199/week @ 2024-06-11 126245/week @ 2024-06-18 125025/week @ 2024-06-25 112380/week @ 2024-07-02 121347/week @ 2024-07-09 115787/week @ 2024-07-16 118182/week @ 2024-07-23 115595/week @ 2024-07-30 122903/week @ 2024-08-06

495,732 每月下载量
1,781 个crate中使用 (8 个直接使用)

MIT 许可证

21KB
190

crates.io docs.rs

dlib

dlib 是一个小型crate,提供宏来简化外部系统库的使用,这些库可以在运行时根据是否启用了某个特性而选择性地加载。

用法

dlib 定义了 external_library! 宏,可以通过以下方式调用

external_library!(feature="dlopen-foo", Foo, "foo",
    statics:
        me: c_int,
        you: c_float,
    functions:
        fn foo() -> c_int,
        fn bar(c_int, c_float) -> (),
        fn baz(*const c_int) -> c_int,
    varargs:
        fn blah(c_int, c_int ...) -> *const c_void,
        fn bleh(c_int ...) -> (),
);

如你所见,需要将静态值与函数以及带有可变参数的函数分开。这三类都是可选的,但使用的必须按照这个顺序出现。函数的返回类型必须都是显式的(因此对于空函数是 -> ())。

如果 feature 参数(在这个例子中,dlopen-foo)指定的特性在你的crate中不存在,这个宏将展开为一个extern块,定义每个项,使用宏的第三个参数作为链接名称

#[link(name = "foo")]
extern "C" {
    pub static me: c_int;
    pub static you: c_float;
    pub fn foo() -> c_int;
    pub fn bar(_: c_int, _: c_float) -> ();
    pub fn baz(_: *const c_int) -> c_int;
    pub fn blah(_: c_int, _: c_int, ...) -> *const c_void;
    pub fn bleh(_: c_int, ...) -> ();
}

如果 feature 参数指定的特性在你的crate中存在,它将展开为宏的第二个参数命名的 struct,其中每个字段对应于定义的每个符号;以及一个名为 open 的方法,该方法尝试根据给定的名称或路径加载库。

pub struct Foo {
    pub me: &'static c_int,
    pub you: &'static c_float,
    pub foo: unsafe extern "C" fn() -> c_int,
    pub bar: unsafe extern "C" fn(c_int, c_float) -> (),
    pub baz: unsafe extern "C" fn(*const c_int) -> c_int,
    pub blah: unsafe extern "C" fn(c_int, c_int, ...) -> *const c_void,
    pub bleh: unsafe extern "C" fn(c_int, ...) -> (),
}


impl Foo {
    pub unsafe fn open(name: &str) -> Result<Foo, DlError> { /* ... */ }
}

此方法在加载成功时返回 Ok(..)。它包含一个具有所有字段指向相应符号的指定结构的实例。

如果 name 指定的库无法打开,它返回 Err(DlError::CantOpen(e)),其中 elibloading 报告的错误(见 [LibLoadingError]);

它还会在第一个缺失的符号上失败,错误信息为:Err(DlError::MissingSymbol(symb)),其中 symb 是一个包含缺失符号名称的 &str

请注意,这种方法是不安全的,因为加载(和卸载)外部C库可能会运行任意代码。因此,您需要确保您想加载的特定库在您想要加载的上下文中是安全的。

在您的crate中保持泛型

如果您想让您的crate在dlopen与链接之间保持泛型,只需在您的 Cargo.toml 中添加一个特性。

[dependencies]
dlib = "0.5"

[features]
dlopen-foo = []

然后,将那个特性的名称作为 feature 参数传递给dlib的宏。

external_library!(feature="dlopen-foo", Foo, "foo",
    functions:
        fn foo() -> c_int,
);

dlib 提供了辅助宏来分发对外部符号的访问。

ffi_dispatch!(feature="dlopen-foo", Foo, function, arg1, arg2);
ffi_dispatch_static!(feature="dlopen-foo", Foo, my_static_var);

这些宏会根据您的crate中是否存在 dlopen-foo 特性来展开为相应的值或函数调用。

您仍然需要确保函数/静态变量或包装结构体 Foo 在作用域内。例如,您可以使用 lazy_static crate 进行初始化,并将包装结构体存储在静态变量中,您可以在需要的地方导入该变量

#[cfg(feature = "dlopen-foo")]
lazy_static::lazy_static! {
    pub static ref FOO_STATIC: Foo =
        Foo::open("libfoo.so").ok().expect("could not find libfoo");
}

然后,它可以使用如下方式在所有使用FFI的模块顶部使用

#[cfg(feature = "dlopen-foo")]
use ffi::FOO_STATIC;
#[cfg(not(feature = "dlopen-foo"))]
use ffi::*;

许可证:MIT

依赖项

~0–5MB