Rust FFI转换工具库ffi-convert-derive的使用:简化C/C++与Rust的跨语言接口开发

Rust FFI转换工具库ffi-convert-derive的使用:简化C/C++与Rust的跨语言接口开发

ffi-convert是一个用于简化Rust和C兼容数据结构之间转换的工具库,包含了一系列实用工具(函数、特性、数据结构等)。

许可证

可选择以下任一许可证:

  • Apache License, Version 2.0
  • MIT license

贡献

除非您明确说明,否则任何有意提交包含在作品中的贡献,如Apache-2.0许可证中所定义,都将按上述双重许可,无需任何附加条款或条件。

安装

在项目目录中运行以下Cargo命令:

cargo add ffi-convert-derive

或在Cargo.toml中添加:

ffi-convert-derive = "0.6.2"

示例代码

以下是一个使用ffi-convert-derive的完整示例:

use ffi_convert::*;

// 定义一个Rust结构体并派生CReprOf
#[derive(AsRust, CReprOf, CDrop)]
#[target_type(MyRustStruct)]
struct MyCStruct {
    pub a: i32,
    pub b: f64,
    pub c: *const u8,  // 字符串指针
    pub d: usize       // 字符串长度
}

// 对应的Rust结构体
#[derive(Debug)]
struct MyRustStruct {
    pub a: i32,
    pub b: f64,
    pub c: String
}

fn main() {
    // 创建C结构体实例
    let rust_string = "Hello FFI".to_string();
    let c_struct = MyCStruct {
        a: 42,
        b: 3.14,
        c: rust_string.as_ptr(),
        d: rust_string.len()
    };
    
    // 转换为Rust结构体
    let rust_struct: MyRustStruct = c_struct.as_rust().unwrap();
    
    println!("Converted struct: {:?}", rust_struct);
    
    // 从Rust结构体创建C结构体
    let new_rust_struct = MyRustStruct {
        a: 100,
        b: 1.618,
        c: "Golden Ratio".to_string()
    };
    
    let c_repr = MyCStruct::c_repr_of(new_rust_struct).unwrap();
    
    // 使用完毕后需要释放资源
    c_repr.c_drop();
}

这个示例展示了如何:

  1. 定义一个C兼容的结构体并派生必要的trait
  2. 将C结构体转换为Rust结构体
  3. 从Rust结构体创建C结构体
  4. 正确处理资源释放

完整示例demo

以下是一个更完整的FFI交互示例,展示了如何在Rust和C之间进行双向数据转换:

use ffi_convert::*;
use std::ffi::CString;

// C兼容的结构体
#[derive(AsRust, CReprOf, CDrop)]
#[target_type(Person)]
struct CPerson {
    name: *const libc::c_char,
    age: libc::c_int,
    height: libc::c_double,
    is_student: bool,
}

// Rust原生结构体
#[derive(Debug, Clone)]
struct Person {
    name: String,
    age: i32,
    height: f64,
    is_student: bool,
}

fn main() {
    // 示例1: 从C结构体创建Rust结构体
    let c_name = CString::new("Alice").unwrap();
    let c_person = CPerson {
        name: c_name.as_ptr(),
        age: 30,
        height: 1.75,
        is_student: false,
    };
    
    let rust_person: Person = c_person.as_rust().unwrap();
    println!("Converted from C: {:?}", rust_person);
    
    // 示例2: 从Rust结构体创建C结构体
    let new_person = Person {
        name: "Bob".to_string(),
        age: 25,
        height: 1.80,
        is_student: true,
    };
    
    let c_repr = CPerson::c_repr_of(new_person.clone()).unwrap();
    
    // 可以再次转换回Rust结构体验证数据
    let converted_back: Person = c_repr.as_rust().unwrap();
    println!("Converted back: {:?}", converted_back);
    
    // 释放资源
    c_repr.c_drop();
    
    // 示例3: 处理Option类型
    #[derive(AsRust, CReprOf, CDrop)]
    #[target_type(OptionalData)]
    struct COptionalData {
        value: *const libc::c_int,
        has_value: bool,
    }
    
    #[derive(Debug)]
    struct OptionalData {
        value: Option<i32>,
    }
    
    let c_data_with_value = COptionalData {
        value: &42 as *const i32,
        has_value: true,
    };
    
    let c_data_without_value = COptionalData {
        value: std::ptr::null(),
        has_value: false,
    };
    
    println!("With value: {:?}", c_data_with_value.as_rust().unwrap());
    println!("Without value: {:?}", c_data_without_value.as_rust().unwrap());
}

这个完整示例展示了:

  1. 字符串和其他基本类型的转换
  2. 布尔值的处理
  3. 双向转换验证
  4. Option类型的处理
  5. 资源管理的最佳实践

文档

更多详细使用说明请参考官方文档。


1 回复

Rust FFI转换工具库ffi-convert-derive的使用:简化C/C++与Rust的跨语言接口开发

介绍

ffi-convert-derive是一个Rust过程宏库,旨在简化Rust与C/C++之间的FFI(外部函数接口)开发。它通过自动生成类型转换代码,减少了手动编写unsafe转换逻辑的工作量,使跨语言接口开发更加安全和便捷。

该库特别适合以下场景:

  • 为现有C/C++库创建Rust绑定
  • 在Rust项目中暴露API供其他语言调用
  • 构建混合语言系统时处理类型转换

主要特性

  1. 自动为结构体生成C兼容的布局
  2. 提供类型间的自动转换
  3. 支持指针和引用类型的处理
  4. 简化错误处理在FFI边界上的传递

安装

在Cargo.toml中添加依赖:

[dependencies]
ffi-convert-derive = "0.1"

基本用法

1. 基本结构体转换

use ffi_convert_derive::{CReprOf, AsRust};

#[derive(Debug, CReprOf, AsRust)]
#[repr(C)]
pub struct Point {
    x: i32,
    y: i32,
}

这会自动生成:

  • Point的C兼容版本PointC(#[repr©])
  • Point::c_repr_of方法,将Rust类型转为C类型
  • PointC::as_rust方法,将C类型转回Rust类型

2. 使用示例

// Rust代码
let rust_point = Point { x: 10, y: 20 };

// 转换为C兼容类型
let c_point = rust_point.c_repr_of();

// 传递给C函数
unsafe {
    some_c_function(&c_point);
}

// 从C接收数据
let c_point_received = unsafe { get_point_from_c() };
let rust_point_received = c_point_received.as_rust();

3. 处理字符串

use ffi_convert_derive::{CReprOf, AsRust};

#[derive(Debug, CReprOf, AsRust)]
#[repr(C)]
pub struct Person {
    name: String,
    age: i32,
}

库会自动处理Rust的String和C的char*之间的转换。

4. 枚举处理

use ffi_convert_derive::{CReprOf, AsRust};

#[derive(Debug, CReprOf, AsRust)]
#[repr(C)]
pub enum Status {
    Ok,
    Error,
    Unknown,
}

5. 错误处理

use ffi_convert_derive::{CReprOf, AsRust, RawPointerConverter};

#[derive(Debug, CReprOf, AsRust, RawPointerConverter)]
pub struct FfiResult<T) {
    is_ok: bool,
    value: T,
    error: String,
}

高级用法

自定义转换逻辑

use ffi_convert_derive::{CReprOf, AsRust};

#[derive(Debug, CReprOf, AsRust)]
#[repr(C)]
pub struct CustomType {
    #[convert_with = "convert_custom_field"]
    field: SomeComplexType,
}

fn convert_custom_field(value: &SomeComplexType) -> Result<SomeComplexTypeC, FfiConvertError> {
    // 自定义转换逻辑
}

处理指针

use ffi_convert_derive::{CReprOf, AsRust, RawPointerConverter};

#[derive(Debug, CReprOf, AsRust, RawPointerConverter)]
pub struct DataWrapper {
    data: *mut u8,
    len: usize,
}

注意事项

  1. 所有FFI相关操作都需要在unsafe块中进行
  2. 确保C端和Rust端的内存管理策略一致
  3. 对于复杂类型,可能需要手动实现部分转换逻辑
  4. 注意处理可能发生的panic,避免跨越FFI边界传播

完整示例

use ffi_convert_derive::{CReprOf, AsRust};

#[derive(Debug, CReprOf, AsRust)]
#[repr(C)]
pub struct User {
    id: u64,
    username: String,
    is_active: bool,
}

// 假设这是从C端接收的函数
extern "C" {
    fn get_user_from_c() -> UserC;
}

fn main() {
    let c_user = unsafe { get_user_from_c() };
    let rust_user: User = c_user.as_rust();
    
    println!("Received user: {:?}", rust_user);
    
    // 发送数据到C端
    let new_user = User {
        id: 42,
        username: "rustacean".to_string(),
        is_active: true,
    };
    
    let c_new_user = new_user.c_repr_of();
    unsafe {
        send_user_to_c(&c_new_user);
    }
}

ffi-convert-derive极大地简化了Rust与C/C++之间的交互,减少了unsafe代码的编写量,同时保持了类型安全和内存安全。

完整示例demo

下面是一个完整的FFI交互示例,展示了如何在Rust和C之间传递复杂数据结构:

// 导入必要的派生宏
use ffi_convert_derive::{CReprOf, AsRust, RawPointerConverter};

// 定义Rust端的复杂结构体
#[derive(Debug, CReprOf, AsRust, RawPointerConverter)]
#[repr(C)]
pub struct Employee {
    id: u64,
    full_name: String,      // 自动转换为C字符串
    department: String,
    salary: f64,
    skills: Vec<String>,    // 自动转换为C字符串数组
    is_manager: bool,
}

// 定义FFI结果类型
#[derive(Debug, CReprOf, AsRust)]
#[repr(C)]
pub struct FfiResult<T> {
    success: bool,
    value: T,
    error_message: String,
}

// 假设的C函数声明
extern "C" {
    // 从C获取员工信息
    fn c_get_employee(employee_id: u64) -> EmployeeC;
    
    // 发送员工信息到C
    fn c_save_employee(employee: *const EmployeeC) -> FfiResultC<u64>;
}

fn main() {
    // 示例1:从C获取数据
    let c_employee = unsafe { c_get_employee(123) };
    let rust_employee: Employee = c_employee.as_rust();
    println!("Received employee: {:?}", rust_employee);

    // 示例2:发送数据到C
    let new_employee = Employee {
        id: 456,
        full_name: "John Doe".to_string(),
        department: "Engineering".to_string(),
        salary: 85000.0,
        skills: vec!["Rust".to_string(), "C++".to_string()],
        is_manager: false,
    };
    
    let c_employee = new_employee.c_repr_of();
    let result = unsafe { c_save_employee(&c_employee) };
    
    let rust_result: FfiResult<u64> = result.as_rust();
    if rust_result.success {
        println!("Employee saved with ID: {}", rust_result.value);
    } else {
        println!("Error saving employee: {}", rust_result.error_message);
    }
}

这个完整示例展示了:

  1. 复杂结构体的自动转换
  2. 字符串和字符串数组的处理
  3. 自定义结果类型的FFI传输
  4. 双向数据传递(Rust到C和C到Rust)
  5. 错误处理机制

使用ffi-convert-derive后,开发者只需关注业务逻辑,而无需手动处理繁琐的类型转换和内存管理细节。

回到顶部