Arrays Ruby FFI使用数组数组调用C函数

Arrays Ruby FFI使用数组数组调用C函数,arrays,ruby,image,pointers,ffi,Arrays,Ruby,Image,Pointers,Ffi,我有以下C接口: int foo(void* bar, void* baz); 基本上,这就是获取一个RGB值数组,处理它们,然后返回一个新的RGB值数组 我为此编写了以下Ruby FFI包装: module MyLibrary extend FFI::Library ffi_lib "path/to/mylibrary.so" attach_function :foo, [:pointer, :pointer], :int end 然而,我并没有真正成功地将Ruby数组传递给这

我有以下C接口:

int foo(void* bar, void* baz);
基本上,这就是获取一个RGB值数组,处理它们,然后返回一个新的RGB值数组

我为此编写了以下Ruby FFI包装:

module MyLibrary
  extend FFI::Library
  ffi_lib "path/to/mylibrary.so"
  attach_function :foo, [:pointer, :pointer], :int
end
然而,我并没有真正成功地将Ruby数组传递给这个FFI包装器。在Ruby中,我有如下内容:

pixels = [[3, 34, 123], [32, 253, 34], ..., [1, 1, 34]]
result = [[0, 0, 0], [0, 0, 0], ..., [0, 0, 0]]

# This does not work!
MyLibrary.foo(pixels, result)

我查看了Ruby FFI文档,但是我没有了解Ruby数组应该如何传递到FFI包装器。

要将数据传递到函数中,需要使用,首先将Ruby数组中的数据复制到函数中,以便C代码看到它时它的格式正确。使用
write\u array\u of.*
方法之一为一维数组复制数据相当简单。对于多维数组来说,这有点棘手,您需要将每个数组复制到
内存指针管理的内存中的正确位置

类似地,对于函数通过指针返回的数据,您需要提供一个大小合适的
MemoryPointer
,然后将数据复制到Ruby数组中。同样,对于使用*
方法的
read\u array\u的一维数组来说,这是相当容易的,对于多维数组来说,这需要做更多的工作

下面是一个简单的例子。这里我假设C函数的参数总是由三个三元素int数组组成–
int[3][3]

C函数:

intfoo(void*bar,void*baz){
//假设两个数组都是[3][3]
int(*bar)[3]=(int(*)bar;
int(*baz_)[3]=(int(*)baz;
//高度复杂的处理-每个条目加倍。
对于(int i=0;i<3;i++){
对于(int j=0;j<3;j++){
baz_[i][j]=2*bar_[i][j];
}
}
返回0;
}
下面是访问它的Ruby代码:

要求“外国金融机构”
模块我的图书馆
扩展FFI::库
ffi_lib“path/to/mylibrary.so”
#给函数一个不同的名称。您可能还希望
#这是私人的。
附加函数(:ffi\u foo,:foo,[:指针,:指针],:int)
#用一个友好的方法包装C函数
#并将数据解包。
def self.foo(像素)
#为输入和输出创建内存指针。他们是
#uint32的9个入口(3*3)数组。
input=FFI::MemoryPointer.new(:uint32,9)
输出=FFI::MemoryPointer.new(:uint32,9)
#将输入数据复制到输入存储器指针中
pixels.each_与_索引do|ary,idx|
#这里的偏移量以字节为单位。int32是4个字节,每个字节
#数组是三个元素,所以总数是3*4=12。
input.put\u数组\u的\u int32(idx*12,ary)
结束
#调用C函数。
ffi_foo(输入、输出)
结果=[]
#将数据复制回Ruby数组。
3.3倍于idx|
结果[[6,68,246],[64,506,68],[2,2,68]]

显然,您需要对其进行调整,以匹配您自己功能的细节。您可能还应该添加错误检查,否则可能会出现错误。

太棒了!谢谢你指出这一点。还有一件事:是否可以使用已经分配的Ruby数组,而不是分配一个单独的指针并将数组转储到那里?目前,数组被复制了两次(在输入和输出时),如果它们很大,这可能会导致一些显著的开销。@linkyndy我认为这是不可能的。尽管Ruby数组由C数组支持(至少在Ruby中是这样),但它是一个Ruby对象数组,不是合适的C类型。你可以把一个MemoryPointer封装在一个类中,给它一个合适的API,让你直接构建C数组,避免Ruby数组。我理解。看起来太麻烦了,至少现在是这样。谢谢