2 个版本
0.1.1 | 2019 年 12 月 17 日 |
---|---|
0.1.0 | 2019 年 12 月 17 日 |
#214 在 FFI
用于 2 crates
22KB
510 行
Foreignc
Foreignc 是一个用于为 Rust 方法自动生成安全的 FFI ABI 的框架。Foreignc 的主要优势是它允许轻松部署和维护安全的 FFI ABI。该 crate 由两部分组成。
- 用于自动生成 FFI ABI 和清理方法的宏
- 用于自动生成 FFI ABI 接收端的函数
- 目前仅支持 Python
快速开始
示例
# pub use foreignc::*;
// Create free methods
generate_free_methods!();
// Evaluates to hello_world() -> CResult<()>
#[with_abi]
pub fn hello_world() {
println!("Hello World");
}
#[derive(Boxed, Debug)]
pub struct BoxedCounter{
value: u32
}
#[with_abi]
impl BoxedCounter {
// Evaluates to new_boxed_counter() -> CResult<*mut BoxedCounter>
pub fn new() -> BoxedCounter{
BoxedCounter {
value: 0
}
}
// Evaluates to inc_boxed_counter(*mut BoxedCounter) -> CResult<()>
pub fn inc(&mut self) {
self.value += 1;
}
// Evaluates to inc_boxed_counter(*mut BoxedCounter) -> CResult<()>
pub fn display(&self) {
println!("{:?}", self);
}
}
上述示例将生成一个 FFI ABI,当调用时返回一个 CResult,指示调用是否成功。当将参数解析到函数中时,使用 FromFFi trait 将从不可安全值转换为安全值。当返回值时,使用 IntoFFi,将类型转换为 FFI 安全值。
模板化
使用 'template' 功能,可以自动生成 FFI 的接收端。
示例
在 cargo.toml 的包部分添加 build = "build.rs"
build.rs
use foreignc::get_package_dir;
fn main() {
// Get all the abis that have been created
let resource = get_package_dir().unwrap();
// Create the python api
resource.generate_python_api("api.py", None).unwrap();
}
运行 cargo build
后,将创建一个 api.py 文件,其外观如下
from __future__ import annotations
from foreignc import *
class BoxedCounter(Box):
@staticmethod
def __free_func__() -> str:
return 'free_boxed_counter'
@create_abi('new_boxed_counter', restype='BoxedCounter')
def new(lib: BaseLib) -> BoxedCounter:
return lib.__lib__.new_boxed_counter().consume()
@create_abi('inc_boxed_counter', argtypes=('BoxedCounter',))
def inc(self) :
return self.__lib__.inc_boxed_counter(self).consume()
@create_abi('display_boxed_counter', argtypes=('BoxedCounter',))
def display(self) :
return self.__lib__.display_boxed_counter(self).consume()
submit_type('BoxedCounter', BoxedCounter)
class MyCrateNameLib(BaseLib):
def __init__(self, src: str):
super().__init__(src)
@create_abi('hello_world_ffi')
def hello_world(self) :
return self.__lib__.hello_world_ffi().consume()
然后使用 Python 运行
- 安装 foreignc
pip install foreignc
- 使用 API
from api import MyCrateNameLib, BoxedCounter
library_path = "C:/Somepath/file.dll"
lib = MyCrateNameLib(library_path)
# Call hello world
lib.hello_world()
counter = BoxedCounter(lib)
# prints 0
counter.display()
counter.inc()
# prints 1
counter.display()
# BoxedCounter droped when the garbage collector run
默认类型
原始类型
以下原始类型受到支持
- bool
- ()
- i8, i16, i32, i64
- u8, u16, u32, u64
- f32, f64
- &str (将转换为 CString)
其他类型
以下其他类型受到支持
- Result (将转换为 CResult)
- Option (将转换为 COption)
- String (将转换为 CString)
自定义结构体
要使用 FFI 屏障解析自定义结构体,请使用 Boxed 或 Json,如下所示
pub use foreignc::*;
generate_free_methods!();
pub use serde::{Serialize, Deserialize};
#[derive(Boxed)]
pub struct BoxedCounter{
value: u32
}
// Wil auto generate free method free_boxed_counter
#[with_abi]
impl BoxedCounter {
pub fn inc(&mut self) {
self.value += 1;
}
}
#[derive(Json, Serialize, Deserialize)]
pub struct JsonCounter{
value: u32
}
impl JsonCounter {
pub fn inc(mut self) -> JsonCounter {
self.value += 1;
self
}
}
封装在Box中的结构体会在堆上存储。Json每次需要解析ffi屏障时,都会使用serde将结构体转换为字符串。
安全性
一般来说,所有分配的内存都需要由创建者释放。这也是generate_free_methods生成函数的基础,该函数为外国结构体分配的内存创建释放函数。以下函数已创建:
- free_string(ptr: *mut CString)
- free_coption(ptr: *mut COption)
- free_cresult(ptr: *mut CResult)
Boxed结构体会自动生成一个释放方法,使用以下约定:free_{to_snake_case(struct name)}
依赖
~10–14MB
~288K SLoC