33 个版本
0.1.12 | 2024 年 8 月 1 日 |
---|---|
0.1.11 | 2024 年 7 月 31 日 |
0.1.9-rc1 | 2024 年 6 月 27 日 |
0.1.6 | 2024 年 3 月 29 日 |
0.0.5 | 2020 年 6 月 10 日 |
#52 in Rust 模式
37,457 每月下载量
在 33 个crate(15 个直接使用) 中使用
400KB
11K SLoC
什么是 safer_ffi
?
safer_ffi
是一个框架,帮助您编写外部的函数接口 (FFI),在不需要在 Rust 代码中使用不安全的代码块的同时,使函数更容易阅读和维护。
先决条件
最低支持的 Rust 版本:1.66.1
快速入门
点击隐藏
小型自包含演示
您可以尝试使用存储库中嵌入的 examples/point
示例
git clone https://github.com/getditto/safer_ffi && cd safer_ffi
(cd examples/point && make)
否则,要开始使用 ::safer_ffi
,请按照以下步骤操作
Crate 布局
步骤 1:Cargo.toml
按照如下修改您的 Cargo.toml
[package]
name = "crate_name"
version = "0.1.0"
edition = "2021"
[lib]
crate-type = [
"staticlib", # Ensure it gets compiled as a (static) C library
# "cdylib", # If you want a shared/dynamic C library (advanced)
"lib", # For `generate-headers` and other downstream rust dependents
# such as integration `tests/`, doctests, and `examples/`
]
[[bin]]
name = "generate-headers"
required-features = ["headers"] # Do not build unless generating headers.
[dependencies]
# Use `cargo add` or `cargo search` to find the latest values of x.y.z.
# For instance:
# cargo add safer-ffi
safer-ffi.version = "x.y.z"
safer-ffi.features = [] # you may add some later on.
[features]
# If you want to generate the headers, use a feature-gate
# to opt into doing so:
headers = ["safer-ffi/headers"]
-
其中
"x.y.z"
应替换为最新发布的版本,您可以通过运行cargo search safer-ffi
来找到。 -
有关更多信息,请参阅 有关
Cargo.toml
的专用章节。
步骤 2:src/lib.rs
然后,要将 Rust 函数导出为 FFI,添加如下所示的 #[derive_ReprC]
和 #[ffi_export]
属性
use ::safer_ffi::prelude::*;
/// A `struct` usable from both Rust and C
#[derive_ReprC]
#[repr(C)]
#[derive(Debug, Clone, Copy)]
pub struct Point {
x: f64,
y: f64,
}
/* Export a Rust function to the C world. */
/// Returns the middle point of `[a, b]`.
#[ffi_export]
fn mid_point(a: &Point, b: &Point) -> Point {
Point {
x: (a.x + b.x) / 2.,
y: (a.y + b.y) / 2.,
}
}
/// Pretty-prints a point using Rust's formatting logic.
#[ffi_export]
fn print_point(point: &Point) {
println!("{:?}", point);
}
// The following function is only necessary for the header generation.
#[cfg(feature = "headers")] // c.f. the `Cargo.toml` section
pub fn generate_headers() -> ::std::io::Result<()> {
::safer_ffi::headers::builder()
.to_file("rust_points.h")?
.generate()
}
- 有关更多信息,请参阅 有关
src/lib.rs
的专用章节。
步骤 3:src/bin/generate-headers.rs
fn main() -> ::std::io::Result<()> {
::crate_name::generate_headers()
}
编译和头文件生成
# Compile the C library (in `target/{debug,release}/libcrate_name.ext`)
cargo build # --release
# Generate the C header
cargo run --features headers --bin generate-headers
- 请参阅专门章节关于头文件生成以获取更多信息。
生成的C头文件(rust_points.h
)
/*! \file */
/*******************************************
* *
* File auto-generated by `::safer_ffi`. *
* *
* Do not manually edit this file. *
* *
*******************************************/
#ifndef __RUST_CRATE_NAME__
#define __RUST_CRATE_NAME__
#ifdef __cplusplus
extern "C" {
#endif
#include <stddef.h>
#include <stdint.h>
/** \brief
* A `struct` usable from both Rust and C
*/
typedef struct Point {
/** <No documentation available> */
double x;
/** <No documentation available> */
double y;
} Point_t;
/** \brief
* Returns the middle point of `[a, b]`.
*/
Point_t
mid_point (
Point_t const * a,
Point_t const * b);
/** \brief
* Pretty-prints a point using Rust's formatting logic.
*/
void
print_point (
Point_t const * point);
#ifdef __cplusplus
} /* extern \"C\" */
#endif
#endif /* __RUST_CRATE_NAME__ */
从C中测试它
以下是一个基本示例,展示如何调用我们导出的Rust函数
main.c
#include <stdlib.h>
#include "rust_points.h"
int
main (int argc, char const * const argv[])
{
Point_t a = { .x = 84, .y = 45 };
Point_t b = { .x = 0, .y = 39 };
Point_t m = mid_point(&a, &b);
print_point(&m);
return EXIT_SUCCESS;
}
编译命令
cc -o main{,.c} -L target/debug -l crate_name -l{pthread,dl,m}
# Now feel free to run the compiled binary
./main
-
有关额外
-l…
标志的说明。这些标志取决于所使用的Rust标准库版本以及编译所使用的系统。为了可靠地知道应该使用哪些标志,应该查询
rustc
。简单命令
rustc --crate-type=staticlib --print=native-static-libs -</dev/null
这将输出到stderr,大致如下
note: Link against the following native artifacts when linking against this static library. The order and any duplication can be significant on some platforms. note: native-static-libs: -lSystem -lresolv -lc -lm -liconv
使用类似
sed -nE 's/^note: native-static-libs: (.*)/\1/p'
的命令可以方便地提取这些标志rustc --crate-type=staticlib --print=native-static-libs -</dev/null \ 2>&1 | sed -nE 's/^note: native-static-libs: (.*)/\1/p'
理想情况下,您不会在真空状态下查询此信息(例如,使用
/dev/null
文件作为上面的输入Rust代码),而是将其应用于您正在编译的实际代码cargo rustc -q -- --print=native-static-libs \ 2>&1 | sed -nE 's/^note: native-static-libs: (.*)/\1/p'
如果您真的想进一步完善,可以使用JSON格式的编译器输出(例如,这可以避免重定向
stderr)。但这时您需要使用JSON解析器,如
jq
RUST_STDLIB_DEPS=$(set -eo pipefail && \ cargo rustc \ --message-format=json \ -- --print=native-static-libs \ | jq -r ' select (.reason == "compiler-message") | .message.message ' | sed -nE 's/^native-static-libs: (.*)/\1/p' \ )
然后使用
cc -o main{,.c} -L target/debug -l crate_name ${RUST_STDLIB_DEPS}
它将输出
Point { x: 42.0, y: 42.0 }
🚀🚀
开发
测试
safer-ffi 包含三个不同的测试套件,可以运行。
# In the project root:
cargo test
# FFI tests
make -C ffi_tests
# JavaScript tests
make -C js_tests
# Running the JS tests also gives you instructions for running browser tests.
# Run this command in the `js_tests` directory, open a browser and navigate to
# https://127.0.0.1:13337/
wasm-pack build --target web && python3 -m http.server 13337
依赖项
~1.6–4.5MB
~77K SLoC