You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

如何在Ruby FFI Gem中处理结构体数组?

使用Ruby FFI调用接收结构体数组的C函数

场景说明

现有C函数定义如下,接收结构体数组和数组长度作为参数:

struct MyStruct {
    char a;
    int b;
    float c;
    double d;
};

void my_function(struct MyStruct *arr, unsigned int num);

已完成Ruby FFI绑定:

module MyModule
    extend FFI::Library
    ffi_lib FFI::Library::LIBC
    ffi_lib "my_c_lib"

    class MyStruct < FFI::Struct
      layout(
          :a, :char, 
          :b, :int, 
          :c, :float, 
          :d, :double
      )
    end

    attach_function :my_function, [ MyStruct.ptr, :uint ], :void
end

需求是传入Ruby数组(元素为MyStruct实例)调用my_function,但尝试以下代码时出现错误:

# 假设arr是填充了MyStruct实例的Ruby数组
ptr = FFI::MemoryPointer.new(MyModule::MyStruct, arr.size)
arr.size.times do | i |
    ptr.put(MyModule::MyStruct, i, arr[i]) # 报错:unsupported type 'MyModule::MyStruct' (ArgumentError)
end
MyModule.my_function(ptr, arr.length)

改用StructByValue后依然报错:

ptr.put(MyModule::MyStruct.by_value, i, arr[i]) # 报错:unsupported type '#<FFI::StructByValue:0x0123456789abcdef' (ArgumentError)

正确实现方式

MemoryPointer#put不支持直接传入结构体类型,需通过结构体实例的内存写入方法完成数据拷贝,以下两种方法均可实现:

方法一:使用write_to_pointer

# 准备填充好数据的MyStruct实例数组
arr = [
  MyModule::MyStruct.new.tap { |s| s[:a] = 1; s[:b] = 2; s[:c] = 3.0; s[:d] = 4.0 },
  MyModule::MyStruct.new.tap { |s| s[:a] = 5; s[:b] = 6; s[:c] = 7.0; s[:d] = 8.0 },
  MyModule::MyStruct.new.tap { |s| s[:a] = 9; s[:b] = 10; s[:c] = 11.0; s[:d] = 12.0 }
]

# 分配容纳整个结构体数组的内存块
ptr = FFI::MemoryPointer.new(MyModule::MyStruct, arr.size)

arr.each_with_index do |struct_inst, idx|
  # 计算当前结构体在内存块中的偏移地址
  struct_ptr = ptr + idx * MyModule::MyStruct.size
  # 将Ruby侧结构体数据写入对应内存位置
  struct_inst.write_to_pointer(struct_ptr)
end

# 调用目标C函数
MyModule.my_function(ptr, arr.length)

方法二:使用MemoryPointer索引写入

# 同样准备好arr数组
ptr = FFI::MemoryPointer.new(MyModule::MyStruct, arr.size)

arr.each_with_index do |struct_inst, idx|
  # 通过索引直接定位内存位置并写入结构体数据
  ptr[idx].write_struct(struct_inst)
end

MyModule.my_function(ptr, arr.length)

补充说明

  • MyStruct.size会返回单个结构体占用的字节数,用于计算内存偏移量
  • 两种方法都会将Ruby侧的结构体实例数据完整拷贝到C侧内存块,确保C函数能正确读取数据

内容的提问来源于stack exchange,提问作者K 3 V R A L

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.08.08 13:15:18