arrays - Ruby FFI to call C function with array of arrays
问题描述
I have the following C interface:
int foo(void* bar, void* baz);
What this does, basically, is take an array of RGB values, process them, and return a new array of RGB values.
I wrote the following Ruby FFI wrapper for it:
module MyLibrary
extend FFI::Library
ffi_lib "path/to/mylibrary.so"
attach_function :foo, [:pointer, :pointer], :int
end
However, I have not really succeeded to pass a Ruby array-of-arrays to this FFI wrapper. In Ruby, I have something like:
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)
I've looked in the Ruby FFI docs, however I did not get how the Ruby arrays should be passed to the FFI wrapper.
解决方案
要将数据传递给您需要使用的函数MemoryPointer
,首先将 Ruby 数组中的数据复制到其中,以便在 C 代码看到它时它的格式正确。使用其中一种方法复制一维数组的数据相当简单write_array_of_*
。对于多维数组,它有点棘手,您需要将每个数组复制到管理到MemoryPointer
.
同样,对于函数通过指针返回的数据,您需要提供MemoryPointer
正确大小的 a,然后将数据复制到 Ruby 数组中。同样,对于具有方法的单维数组来说,这相当容易,而对于多维数组来说,这read_array_of*
需要更多的工作。
这是一个简单的例子。在这里,我假设 C 函数的参数始终由三个三元素 int 数组组成 - int[3][3]
。
C函数:
int foo(void* bar, void* baz) {
// assume both arrays are [3][3]
int(*bar_)[3] = (int (*)[3]) bar;
int(*baz_)[3] = (int (*)[3]) baz;
// Highly complex processing - double each entry.
for (int i = 0; i< 3; i++) {
for (int j = 0; j < 3; j++) {
baz_[i][j] = 2 * bar_[i][j];
}
}
return 0;
}
这是访问它的 Ruby 代码:
require 'ffi'
module MyLibrary
extend FFI::Library
ffi_lib "path/to/mylibrary.so"
# Give function a different name. You might also want to make
# it private.
attach_function(:ffi_foo, :foo, [:pointer, :pointer], :int)
# Wrap the C function with a friendly method that packages
# and unpackages the data.
def self.foo(pixels)
# Create the MemoryPointers for input and output. They are
# both 9 entry (3 * 3) arrays of uint32.
input = FFI::MemoryPointer.new(:uint32, 9)
output = FFI::MemoryPointer.new(:uint32, 9)
# Copy the input data into the input MemoryPointer
pixels.each_with_index do |ary, idx|
# The offset here is in bytes. int32 is 4 bytes, each
# array is three elements so total is 3 * 4 = 12.
input.put_array_of_int32(idx * 12, ary)
end
# Call the C function.
ffi_foo(input, output)
result = []
# Copy the data back into a Ruby array.
3.times do |idx|
result << output.get_array_of_int32(idx * 12, 3)
end
# Return the final result
result
end
end
然后你可以像这样使用它:
pixels = [[3, 34, 123], [32, 253, 34], [1, 1, 34]]
p MyLibrary.foo(pixels) #=>[[6, 68, 246], [64, 506, 68], [2, 2, 68]]
显然,您需要对其进行调整以匹配您自己功能的细节。您可能还应该添加错误检查,否则您可能会遇到段错误。
推荐阅读
- python - 检查失败:1 == NumElements()(1 vs. 2)必须有一个单元素张量(神经网络度量)
- sas - SAS计算百分位数并保存到宏变量
- graphql - Prisma deploy:枚举期望关系/期望接口、指令或定义
- python-2.7 - 当您调用具有不同参数的函数时会发生什么
- swift - 每次单击单元格时都会填充快速进度条
- keras - 如何设置 Keras TimeseriesGenerator 来预测第二个下一个值?
- java - JsonPath SUPPRESS_EXCEPTIONS 似乎不起作用
- java - 尝试使用 java CertStore 从 Windows 活动目录中检索 userCertificate 但没有成功
- javascript - 如何从响应中获取实际数据?我不确定
- python - 如何在 Python 的嵌套列表理解的内部循环中使用 if...else 条件?