Ruby FFI:多维数组

时间:2014-03-25 17:45:45

标签: c ruby arrays ffi

我试图从Ruby调用的C函数是这样的:

void foo(double *in_array, double *out_array)

其中:

  • in_array是“foo”将使用的数组数组 计算并返回:
  • out_array也是一个数组数组,C函数将改变其内容。

我的包装器看起来像这样:

module FooLib
  extend FFI::Library
  ffi_lib "foo.so"
  attach_function :Foo, [:pointer, :pointer], :void
end

我在Ruby中做了以下事情:

# Allocate the objects and prepare them    
in_array = Matrix.build(10, 3) { rand }.to_a
out_array = Matrix.build(10, 3) { 0 }.to_a
FooLib.Foo(in_array, out_array)

但是我收到以下错误:

:pointer argument is not a valid pointer (ArgumentError)

我可以理解我需要使用指向这些数组的指针而不是数组对象,但我不知道如何做到这一点。这是否意味着我需要使用LibC包装器在C中创建这些结构?

3 个答案:

答案 0 :(得分:2)

Per Momer的回答,看起来你确实需要使用LibC包装器。将多维数组转换为正确的指针并不简单,所以我想我会把它放在这里,以防它帮助其他人:

in_array = Matrix.build(10, 3) { rand }.to_a
in_array_flattened = in_array.transpose.flatten # Just flatten your multi-dim array
in_array_ptr = LibC.malloc(FFI.type_size(FFI::TYPE_FLOAT64) * in_array_flattened.size) # Watchout the type you want to use.
in_array_ptr.write_array_of_double(in_array.flatten)

# Same for out_array

FooLib.Foo(in_array_ptr, out_array_ptr)

# Convert back to Ruby
values = in_array_ptr.read_array_of_double(in_array_flattened.length)
values = values.enum_for(:each_slice, 10).to_a.transpose # Might be the C lib I am using but you do need to do this conversion in my case to get the multi-dim array you are expecting

答案 1 :(得分:1)

当您需要FFI指针时,只需声明一个。

# some_pointer = FFI::MemoryPointer.new(:type, size)
some_pointer = FFI::MemoryPointer.new(:double, 8)

这适用于单个变量。我们都需要查询阵列的FFI文档。 http://rubydoc.info/github/ffi/ffi/FFI 关于数组指针必须有一些东西。 我的问题类似于I am familiar with Ruby /DL but not sure how to use the C function calls that have pointers for return parameters

答案 2 :(得分:0)

FFI documentation about pointers.

详细解释了发生在您身上的事情

直接来自该文件:

某些情况需要分配本机内存并将该缓冲区移交给外部库。然后外部库处理该缓冲区的生命周期,包括最终释放它。

包装libc并使用其malloc和free函数来分配和释放本机内存。

module LibC
  extend FFI::Library
  ffi_lib FFI::Library::LIBC

  # memory allocators
  attach_function :malloc, [:size_t], :pointer
  attach_function :calloc, [:size_t], :pointer
  attach_function :valloc, [:size_t], :pointer
  attach_function :realloc, [:pointer, :size_t], :pointer
  attach_function :free, [:pointer], :void

  # memory movers
  attach_function :memcpy, [:pointer, :pointer, :size_t], :pointer
  attach_function :bcopy, [:pointer, :pointer, :size_t], :void

end # module LibC

在ruby代码中,对这些函数的调用将返回FFI :: Pointers。使用FFI :: Pointer上定义的方法将数据从ruby内存移动到本机内存。

foo = "a ruby string"
bar = 3.14159
baz = [1, 2, 3, 4, 5]

buffer1 = LibC.malloc foo.size
buffer1.write_string foo

buffer2 = LibC.malloc bar.size
buffer2.write_float bar

# all of the array elements need to be the same type
# meaning you can't mix ints, floats, strings, etc.
buffer3 = LibC.malloc(baz.first.size * baz.size)
buffer3.write_array_of_int baz