Чтобы передать данные в функцию, вам нужно использовать MemoryPointer
, сначала скопировав в нее данные из массивов Ruby, чтобы они были в правильной форме, когда код C видитЭто.Копировать данные для одномерного массива довольно просто, используя один из методов write_array_of_*
.Для многомерного массива это немного сложнее, вам нужно скопировать каждый массив в правильное место в памяти, управляемой в MemoryPointer
.
Аналогично для данных, возвращаемых функцией через указатель, вам нужнопредоставьте MemoryPointer
правильного размера и затем скопируйте данные в массивы 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]]
Очевидно, вам необходимо адаптировать его для соответствия деталям вашей собственной функции.Возможно, вам также следует добавить проверку ошибок, иначе вы можете получить segfaults.