#foreignc #methods #create #part #generate #abi #abis

foreignc_derive

为推导ffi abis创建方法。作为foreignc crate的一部分制作

2个版本

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

#159 in #abi


用于 foreignc

MIT/Apache

35KB
797

Foreignc

Latest Version Rust Documentation

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

  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屏障解析自定义结构体,请使用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