2014-03-25 93 views
1

我试图从红宝石调用C函数是这样的:红宝石FFI:多维数组

void foo(double *in_array, double *out_array) 

其中:

  • in_array是数组的数组将由被用于“foo”到 计算并返回:
  • ​​这也是一个数组数组,C函数将改变其内容。

我的包装看起来是这样的:

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

而且我做的红宝石如下:

# 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中创建这些结构?

回答

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 
+0

谢谢Momer。我已经看到了这一点(每次我引用LibC包装器的问题结束时)。但是一旦你拥有了这个功能,它就不是直截了当的了(我认为)你如何将它应用到多维数组中。我将添加一个解释如何做到这一点的答案。 – PJC

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

当你需要为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

+0

是的,绝对。我的意思是更新我的答案,但你可以简单地做'in_array_ptr = FFI :: MemoryPointer.new(FFI。type_size(FFI :: TYPE_FLOAT64)* in_array_flattened.size)'。这就是我最终做到的。 – PJC

+0

好。很高兴你找到答案。感谢所有的投票。 –