2014-03-25 145 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

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