2 个版本

0.1.1 2019 年 12 月 17 日
0.1.0 2019 年 12 月 17 日

#2238Rust 模式


3 个包(2 个直接) 使用

MIT/Apache

3KB
62

Foreignc

Latest Version Rust Documentation

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 运行

  1. 安装 foreignc pip install foreignc
  2. 使用 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)}

无运行时依赖