如何在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
相关产品推荐
相关产品推荐

