2 个版本
0.1.1 | 2019 年 12 月 17 日 |
---|---|
0.1.0 | 2019 年 12 月 17 日 |
#2238 在 Rust 模式
被 3 个包(2 个直接) 使用
3KB
62 行
Foreignc
Foreignc 是一个用于自动生成 Rust 方法安全 FFI ABI 的框架。Foreignc 的主要优势在于它允许轻松部署和维护安全 FFI ABI。该包由两部分组成。
- 用于自动生成 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 特性将不安全值转换为安全值。当返回值时,使用 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 障碍的 custom struct,请使用 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 在每次需要解析 FFI 障碍时,都会使用 serde 将结构体转换为字符串
安全性
一般来说,所有分配的内存都需要由创建者释放。这也是 generate_free_methods 的基础,它创建用于释放由 foreignc 创建的结构体分配的内存的函数。以下函数被创建:
- free_string(ptr: *mut CString)
- free_coption(ptr: *mut COption)
- free_cresult(ptr: *mut CResult)
使用以下约定自动生成释放方法的boxed结构体:free_{to_snake_case(struct name)}