Ruby FFI 用数组调用 C 函数
Ruby FFI to call C function with array of arrays
我有以下 C 接口:
int foo(void* bar, void* baz);
基本上,它的作用是获取一个 RGB 值数组,对其进行处理,然后 return 一个新的 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 包装器。
要将数据传递给您需要使用 MemoryPointer
的函数,首先将数据从 Ruby 数组复制到其中,以便在 C代码看到它。使用 write_array_of_*
方法之一复制一维数组的数据非常简单。对于多维数组,它有点棘手,您需要将每个数组复制到内存管理到 MemoryPointer
.
的正确位置
类似地,对于函数通过指针返回的数据,您需要提供大小合适的 MemoryPointer
,然后将数据复制到 Ruby 数组中。同样,对于使用 read_array_of*
方法的单维数组来说,这是相当容易的,而对于多维数组来说,这需要更多的工作。
这是一个简单的例子。这里我假设 C 函数的参数总是由三个 three-element 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]]
显然,您需要对此进行调整以匹配您自己的功能的细节。您可能还应该添加错误检查,否则您可能会遇到段错误。
我有以下 C 接口:
int foo(void* bar, void* baz);
基本上,它的作用是获取一个 RGB 值数组,对其进行处理,然后 return 一个新的 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 包装器。
要将数据传递给您需要使用 MemoryPointer
的函数,首先将数据从 Ruby 数组复制到其中,以便在 C代码看到它。使用 write_array_of_*
方法之一复制一维数组的数据非常简单。对于多维数组,它有点棘手,您需要将每个数组复制到内存管理到 MemoryPointer
.
类似地,对于函数通过指针返回的数据,您需要提供大小合适的 MemoryPointer
,然后将数据复制到 Ruby 数组中。同样,对于使用 read_array_of*
方法的单维数组来说,这是相当容易的,而对于多维数组来说,这需要更多的工作。
这是一个简单的例子。这里我假设 C 函数的参数总是由三个 three-element 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]]
显然,您需要对此进行调整以匹配您自己的功能的细节。您可能还应该添加错误检查,否则您可能会遇到段错误。