3个版本
0.1.4 | 2023年12月7日 |
---|---|
0.1.1 | 2023年10月22日 |
0.1.0 | 2023年10月22日 |
在 过程宏 中排名第1143
每月下载量 29 次
4KB
ferment
FFI语法树变形工具(开发中)
允许生成符合FFI规范的rust类型(结构体、枚举、类型、函数)的等效项。
该项目是一个由几个crate组成的rust工作空间
ferment-interfaces
: 提供从/到FFI兼容类型转换方法的一些特性和辅助函数和结构ferment-macro
: 一个过程宏,它只捕获基于syn的项的目标代码。ferment-example
: 提供基本示例。ferment-example-nested
: 提供依赖于ferment crate的示例。ferment
: 一个使用syn
crate的强大功能来变形FFI兼容语法树的工具。
过程宏由2个宏组成
export
- 用于结构体/枚举/函数/类型register
- 用于自定义定义的转换
用法
crate尚未发布,因此请在本地示例中使用它
ferment-interfaces = { path = "../../ferment/ferment-interfaces" }
ferment-macro = { path = "../../ferment/ferment-macro" }
ferment = { path = "../../ferment/ferment" }
使用该工具意味着使用cbindgen
,配置如下
extern crate cbindgen;
fn main() {
extern crate cbindgen;
extern crate ferment;
use std::process::Command;
fn main() {
match ferment::Builder::new()
.with_mod_name("fermented")
.with_crates(vec![])
.generate() {
Ok(()) => match Command::new("cbindgen")
.args(&["--config", "cbindgen.toml", "-o", "target/example.h"])
.status() {
Ok(status) => println!("Bindings generated into target/example.h with status: {}", status),
Err(err) => panic!("Can't generate bindings: {}", err)
}
Err(err) => panic!("Can't create FFI expansion: {}", err)
}
}
}
示例
对于标记为导出的特性,例如这样
#[ferment_macro::export]
pub trait IHaveChainSettings {
fn name(&self) -> String;
}
您还可以使用以逗号分隔的特名字符串的宏
#[ferment_macro::export(IHaveChainSettings)]
pub enum ChainType {
MainNet,
TestNet,
DevNet(DevnetType)
}
这将公开特定类型的特性方法绑定
对于带有ferment::export
标签的结构体
#[derive(Clone)]
#[ferment_macro::export]
pub struct LLMQSnapshot {
pub member_list: Vec<u8>,
pub skip_list: Vec<i32>,
pub skip_list_mode: LLMQSnapshotSkipMode,
pub option_vec: Option<Vec<u8>>,
}
将生成以下具有FFI兼容字段和相应的从/到转换的代码
#[doc = "FFI-representation of the "crate::model::snapshot::LLMQSnapshot""]
#[repr(C)]
#[derive(Clone)]
#[allow(non_camel_case_types)]
pub struct LLMQSnapshot {
pub member_list: *mut crate::fermented::generics::Vec_u8,
pub skip_list: *mut crate::fermented::generics::Vec_i32,
pub skip_list_mode: *mut crate::fermented::types::model::snapshot::LLMQSnapshotSkipMode,
pub option_vec: *mut crate::fermented::generics::Vec_u8,
}
impl ferment_interfaces::FFIConversion<crate::model::snapshot::LLMQSnapshot> for LLMQSnapshot {
unsafe fn ffi_from_const(ffi: *const LLMQSnapshot) -> crate::model::snapshot::LLMQSnapshot {
let ffi_ref = &*ffi;
crate::model::snapshot::LLMQSnapshot {
member_list: ferment_interfaces::FFIConversion::ffi_from(ffi_ref.member_list),
skip_list: ferment_interfaces::FFIConversion::ffi_from(ffi_ref.skip_list),
skip_list_mode: ferment_interfaces::FFIConversion::ffi_from(ffi_ref.skip_list_mode),
option_vec: ferment_interfaces::FFIConversion::ffi_from_opt(ffi_ref.option_vec),
}
}
unsafe fn ffi_to_const(obj: crate::model::snapshot::LLMQSnapshot) -> *const LLMQSnapshot {
ferment_interfaces::boxed(LLMQSnapshot {
member_list: ferment_interfaces::FFIConversion::ffi_to(obj.member_list),
skip_list: ferment_interfaces::FFIConversion::ffi_to(obj.skip_list),
skip_list_mode: ferment_interfaces::FFIConversion::ffi_to(obj.skip_list_mode),
option_vec: match obj.option_vec {
Some(vec) => ferment_interfaces::FFIConversion::ffi_to(vec),
None => std::ptr::null_mut(),
},
})
}
unsafe fn destroy(ffi: *mut LLMQSnapshot) {
ferment_interfaces::unbox_any(ffi);
}
}
impl Drop for LLMQSnapshot {
fn drop(&mut self) {
unsafe {
let ffi_ref = self;
ferment_interfaces::unbox_any(ffi_ref.member_list);
ferment_interfaces::unbox_any(ffi_ref.skip_list);
<crate::fermented::types::model::snapshot::LLMQSnapshotSkipMode as ferment_interfaces::FFIConversion<crate::model::snapshot::LLMQSnapshotSkipMode>>::
destroy(ffi_ref.skip_list_mode);
if !ffi_ref.option_vec.is_null() {
ferment_interfaces::unbox_any(ffi_ref.option_vec);
};
}
}
}
#[doc = "# Safety"]
#[allow(non_snake_case)]
#[no_mangle]
pub unsafe extern "C" fn LLMQSnapshot_ctor(
member_list: *mut crate::fermented::generics::Vec_u8,
skip_list: *mut crate::fermented::generics::Vec_i32,
skip_list_mode: *mut crate::fermented::types::model::snapshot::LLMQSnapshotSkipMode,
option_vec: *mut crate::fermented::generics::Vec_u8)
-> *mut LLMQSnapshot {
ferment_interfaces::boxed(LLMQSnapshot {
member_list,
skip_list,
skip_list_mode,
option_vec,
})
}
#[doc = "# Safety"]
#[allow(non_snake_case)]
#[no_mangle]
pub unsafe extern "C" fn LLMQSnapshot_destroy(ffi: *mut LLMQSnapshot) {
ferment_interfaces::unbox_any(ffi);
}
对于带有export
标签的函数
#[ferment_macro::export]
pub fn address_with_script_pubkey(script: Vec<u8>) -> Option<String> {
Some(format_args!("{0:?}", script).to_string())
}
将生成以下代码
#[doc = "FFI-representation of the "address_with_script_pubkey""]
#[doc = "# Safety"]
#[no_mangle]
pub unsafe extern "C" fn ffi_address_with_script_pubkey(script: *mut crate::fermented::generics::Vec_u8) -> *mut std::os::raw::c_char {
let conversion = ferment_interfaces::FFIConversion::ffi_from(script);
let obj = crate::example::address::address_with_script_pubkey(conversion);
ferment_interfaces::FFIConversion::ffi_to_opt(obj)
}
对于带有export
标签的类型别名
#[ferment::export]
pub type HashID = [u8; 32];
以下代码将在 crate::fermented::types::*
中生成,具有类似的转换和绑定
#[repr(C)]
#[derive(Clone, Debug)]
pub struct HashID(*mut [u8; 32]);
对于带有 export
标记的特质
#[ferment_macro::export]
pub trait IHaveChainSettings {
// ..
}
将生成 vtable 和特质对象
#[repr(C)]
#[derive(Clone)]
#[allow(non_camel_case_types)]
pub struct IHaveChainSettings_VTable {
// ..
}
#[repr(C)]
#[derive(Clone)]
#[allow(non_camel_case_types)]
pub struct IHaveChainSettings_TraitObject {
pub object: *const (),
pub vtable: *const IHaveChainSettings_VTable,
}
以及它们的实现者的绑定,如下所示
#[doc = "# Safety"]
#[allow(non_snake_case)]
#[no_mangle]
pub extern "C" fn ChainType_as_IHaveChainSettings_TraitObject(
obj: *const crate::chain::common::chain_type::ChainType)
-> IHaveChainSettings_TraitObject {
IHaveChainSettings_TraitObject {
object: obj as *const (),
vtable: &ChainType_IHaveChainSettings_VTable,
}
}
#[doc = "# Safety"]
#[allow(non_snake_case)]
#[no_mangle]
pub unsafe extern "C" fn ChainType_as_IHaveChainSettings_TraitObject_destroy(obj: IHaveChainSettings_TraitObject) {
ferment_interfaces::unbox_any(obj.object as *mut crate::chain::common::chain_type::ChainType);
}
使用此代码,cbindgen 将能够生成绑定
struct IHaveChainSettings_TraitObject ChainType_as_IHaveChainSettings_TraitObject(const struct ChainType *obj);
void ChainType_as_IHaveChainSettings_TraitObject_destroy(struct IHaveChainSettings_TraitObject obj);
当前限制
- 我们应该使用宏定义标记所有涉及导出的结构
- 处理类型别名存在一些困难。因此,如果可能,应避免使用它们。因为,为了保证可以处理,必须将其封装在未命名的结构体中。这通常比直接使用该类型效率低。例如,
pub type KeyID = u32
变为pub struct KeyID_FFI(u32)
。将在某个时候提供支持。
泛型混淆规则
转换遵循一些混淆规则,并为 ffi 结构给出名称。翻译名称的示例
Vec<u8>
->Vec_u8
Vec<u32>
->Vec_u32
Vec<Vec<u32>>
->Vec_Vec_u32
BTreeMap<HashID, Vec<u32>>
->std_collections_Map_keys_crate_HashID_values_Vec_u32
BTreeMap<HashID, Vec<u32>>
->std_collections_Map_keys_u32_values_Vec_u32
BTreeMap<HashID, BTreeMap<HashID, Vec<u32>>>
->std_collections_Map_keys_crate_HashID_values_std_collections_Map_keys_crate_HashID_values_Vec_u32
- 等等
然后宏实现了这些结构所需的必要转换。例如 BTreeMap<HashID, Vec<HashID>>
#[repr(C)]
#[derive(Clone)]
#[allow(non_camel_case_types)]
pub struct std_collections_Map_keys_crate_nested_HashID_values_Vec_crate_nested_HashID {
pub count: usize,
pub keys: *mut *mut crate::fermented::types::nested::HashID,
pub values: *mut *mut crate::fermented::generics::Vec_crate_nested_HashID,
}
impl ferment_interfaces::FFIConversion<std::collections::BTreeMap<crate::nested::HashID, Vec<crate::nested::HashID>>>
for std_collections_Map_keys_crate_nested_HashID_values_Vec_crate_nested_HashID {
unsafe fn ffi_from_const(
ffi: *const std_collections_Map_keys_crate_nested_HashID_values_Vec_crate_nested_HashID)
-> std::collections::BTreeMap<crate::nested::HashID, Vec<crate::nested::HashID>> {
let ffi_ref = &*ffi;
ferment_interfaces::from_complex_map(ffi_ref.count, ffi_ref.keys, ffi_ref.values)
}
unsafe fn ffi_to_const(
obj: std::collections::BTreeMap<crate::nested::HashID, Vec<crate::nested::HashID>>)
-> *const std_collections_Map_keys_crate_nested_HashID_values_Vec_crate_nested_HashID {
ferment_interfaces::boxed(Self {
count: obj.len(),
keys: ferment_interfaces::to_complex_vec(obj.keys().cloned()),
values: ferment_interfaces::to_complex_vec(obj.values().cloned()),
})
}
unsafe fn destroy(ffi: *mut std_collections_Map_keys_crate_nested_HashID_values_Vec_crate_nested_HashID) {
ferment_interfaces::unbox_any(ffi);
}
}
impl Drop for std_collections_Map_keys_crate_nested_HashID_values_Vec_crate_nested_HashID {
fn drop(&mut self) {
unsafe {
ferment_interfaces::unbox_any_vec_ptr(self.keys, self.count);
ferment_interfaces::unbox_any_vec_ptr(self.values, self.count);
}
}
}
最终生成的代码将放置在配置中指定的文件中,如下所示
pub mod types {
// package relationships are inherited
// so type like crate::some_module::SomeStruct will be expanded like this:
pub mod some_module {
pub struct SomeStruct {
// ...
}
}
}
pub mod generics {
// We expand generic types separately here to avoid duplication
#[allow(non_camel_case_types)]
pub struct std_collections_Map_keys_crate_nested_HashID_values_Vec_crate_nested_HashID {
// ..
}
}
手动转换支持
- 我们可以使用
[ferment_macro::register(SomeFFIIncompatibleStructOrWhatever)]
- 这允许我们手动为类型创建自定义转换。
- 这对于非发酵代码(如来自 rust std 库或任何其他第三方crate的类型)尤为重要。
- 示例
依赖项
~1.5MB
~35K SLoC