#ffi #macro #generator #wrapper #unwind-safe

easy_ffi

一个针对 Rust 的可安全展开的 ffi 包装器生成器

1 个不稳定版本

使用旧的 Rust 2015

0.1.0 2018年2月25日

#742#ffi

MIT/Apache

9KB
65

Docs Crates.io

一个针对 Rust 的可安全展开的 ffi 包装器生成器

更多详情请参阅文档

许可证

许可协议为以下之一

贡献

除非你明确表示,否则根据 Apache-2.0 许可证定义的,你有意提交的任何贡献,都应按照上述方式双许可,不附加任何额外条款或条件。


lib.rs:

Easy-FFI:一个 FFI 辅助宏的帮助宏

这个包试图使编写可安全展开的 C API 的过程更加人性化。

这个包的功能

  • 防止跨 FFI 边界展开
  • 允许使用通常的 Rust 错误处理习惯用法

这个包的功能 包括

  • 防止你取消引用无效指针
  • 防止内存泄露
  • 对你 FFI 函数的参数或返回值的任何类型验证

示例

没有 easy_ffi

fn thing_that_could_fail_or_panic() -> Result<i32, &'static str> {
    // Do stuff...
}

#[no_mangle]
pub extern "C" fn my_ffi_function(i: i32) -> i32 {
    // Unwinding over the FFI boundary is UB, so we need to catch panics
    let panic_result: Result<i32, _> = ::std::panic::catch_unwind(move || {
        let result_one = thing_that_could_fail_or_panic();

        // We need to match on this result to handle the possible Result::Err
        // and convert it to a senssible ffi representation.
        match result_one {
            Ok(actual) => return actual,
            Err(e) => {
                println!("Oops! {:?}", e);
                return -1;
            }
        }
    });

    // Then, we need to match on the catch_unwind result again like we did for the Result::Err
    match panic_result {
        Ok(actual) => return actual,
        Err(_e) => {
            println!("unexpected panic!");
            return -1;
        }
    }
}

仅使用 rust std,任何可能引发恐慌的东西都需要用 catch_unwind 包装来防止进入 C。另外,由于 FFI 函数不会返回 Rust 的 Result<T, E>,你无法使用 try!? 进行错误处理。

使用 easy_ffi


fn thing_that_could_fail_or_panic() -> Result<i32, &'static str> {
    // Do stuff...
}

// This defines a new macro that will be used to wrap a more "rusty"
// version of our ffi function.
easy_ffi!(my_ffi_fn =>
    // Now we define a handler for each of the error cases: A `Result::Err` and
    // a caught panic. `Result::Err` comes first:
    |err| {
        println!("{}", err);
        // The handler still needs to return the actual type that the C api expects,
        // so we're going to do so here:
        -1
    }
    // Next, the panic. This will have the type `Box<Any + Send + 'static>`. See
    // `::std::panic::catch_unwind` for more details.
    |panic_val| {
        match panic_val.downcast_ref::<&'static str>() {
            Some(s) => println!("panic: {}", s),
            None => println!("unknown panic!"),
        }
        // As with the error handler, the panic handler also needs to return
        // the real ffi return type.
        -1
    }
);

// Using the new macro that `easy_ffi!` created for us, we can write our
// function just like any Rust function that returns a `Result`. This will
// automatically be wrapped in a `catch_unwind`, and error handling will be
// left to the "handler" that was defined in the call to `easy_ffi`.
my_ffi_fn!(
    /// You can put doc comments here!
    ///
    /// This should generate a function with the signature `fn(i32) -> i32`,
    /// with all of the necessary `pub`, `#[no_mangle]`, `extern "C"`, etc.
    fn foo(i: i32) -> Result<i32, &'static str> {
        thing_that_could_fail_or_panic()
    }
);

无运行时依赖