2个版本
0.1.1 | 2019年12月17日 |
---|---|
0.1.0 | 2019年12月17日 |
#159 in #abi
用于 foreignc
35KB
797 行
Foreignc
Foreignc是一个用于自动生成Rust方法安全的ffi abis的框架。Foreignc的主要优势是允许轻松部署和维护安全的ffi abis。该crate由两部分组成。
- 用于自动生成ffi abis和清理方法的宏
- 用于自动生成ffi abis接收端的功能
- 目前仅支持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 abis,当调用时返回一个CResult,指示调用是否成功结束。当参数解析为函数时,使用FromFFi trait将不安全的值转换为安全的值。当返回值时,使用IntoFFi将类型转换为ffi安全的值。
模板化
使用“template”特性,可以自动生成ffi的接收端。
示例
将 build = "build.rs"
添加到 cargo.toml 的包部分
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
}
}
Boxed结构体被包装在一个在堆上存储结构的Box中。Json使用serde将结构体转换为字符串,每次需要解析ffi屏障时都会这样做
安全性
一般来说,所有分配的内存都需要由创建者释放。这也是生成_free_methods的基础,该函数用于创建释放由foreignc构建的结构体分配的内存的函数。以下是创建的函数:
- free_string(ptr: *mut CString)
- free_coption(ptr: *mut COption)
- free_cresult(ptr: *mut CResult)
使用以下约定自动生成boxed结构的free方法:free_{to_snake_case(struct name)}
依赖项
~1.2–3.5MB
~80K SLoC