2015-04-01 10 views
5

Chcę użyć ruby ​​ffi gem, aby wywołać funkcję c, która ma tablicę jako zmienną wejściową, a wyjściem jest tablica. Oznacza to, że funkcja C wygląda następująco:Jak radzić sobie z tablicami ruby ​​w klejnocie ruby ​​ffi?

double *my_function(double array[], int size) 

Stworzyłem rubin wiążący jako:

module MyModule 
    extend FFI::Library 
    ffi_lib 'c' 
    ffi_lib 'my_c_lib' 
    attach_function :my_function, [:pointer, int], :pointer 

będę chciał nawiązać połączenie w kodzie Ruby jak:

result_array = MyModule.my_function([4, 6, 4], 3) 

Jak mam to zrobić?

Odpowiedz

4

Powiedzmy, że jest to biblioteka chcesz użyć w skrypcie rubinowym, nazywają to my_c_lib.c:

#include <stdlib.h> 

double *my_function(double array[], int size) 
{ 
    int i = 0; 
    double *new_array = malloc(sizeof(double) * size); 
    for (i = 0; i < size; i++) { 
    new_array[i] = array[i] * 2; 
    } 

    return new_array; 
} 

Można go skompilować tak:

$ gcc -Wall -c my_c_lib.c -o my_c_lib.o 
$ gcc -shared -o my_c_lib.so my_c_lib.o 

Teraz jest gotowy do użyj swojego kodu ruby ​​(my_c_lib.rb):

require 'ffi' 

module MyModule 
    extend FFI::Library 

    # Assuming the library files are in the same directory as this script 
    ffi_lib "./my_c_lib.so" 

    attach_function :my_function, [:pointer, :int], :pointer 
end 

array = [4, 6, 4] 
size = array.size 
offset = 0 

# Create the pointer to the array 
pointer = FFI::MemoryPointer.new :double, size 

# Fill the memory location with your data 
pointer.put_array_of_double offset, array 

# Call the function ... it returns an FFI::Pointer 
result_pointer = MyModule.my_function(pointer, size) 

# Get the array and put it in `result_array` for use 
result_array = result_pointer.read_array_of_double(size) 

# Print it out! 
p result_array 

A oto wynik z uruchomieniem skryptu:

$ ruby my_c_lib.rb 
[8.0, 12.0, 8.0] 

Uwaga na zarządzanie pamięcią ... od docs https://github.com/ffi/ffi/wiki/Pointers:

FFI :: klasa MemoryPointer przydziela rodzimą pamięć automatycznego zbierania śmieci jako słodzik. Gdy MemoryPointer wykracza poza zakres, pamięć jest zwalniana w ramach procesu czyszczenia pamięci.

Nie powinieneś więc bezpośrednio dzwonić pod numer pointer.free. Ponadto, tylko w celu sprawdzenia, czy trzeba było ręcznie wolne result_pointer, zadzwoniłem result_pointer.free po wydrukowaniu wydobywania tablicę i dostał to ostrzeżenie

warning: calling free on non allocated pointer #<FFI::Pointer address=0x007fd32b611ec0> 

Wygląda więc na to, że nie trzeba ręcznie wolne result_pointer albo.

Powiązane problemy