Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

How do I handle ruby arrays in ruby ffi gem?

Tags:

c

ruby

ffi

I want to use the ruby ffi gem to call a c function which has an array as an input variable and the output is an array. That is, the c function looks like:

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

I have created the ruby binding as:

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

I will like to make a call in ruby code like:

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

How do I go about this?

like image 713
Lekeasong Avatar asked Apr 01 '15 11:04

Lekeasong


1 Answers

Let's say that this is the library you wish to use in your ruby script, call it 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;
}

You can compile it like so:

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

Now, it's ready to use in in your ruby code (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

And here is the result of running the script:

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

A note on memory management...from the docs https://github.com/ffi/ffi/wiki/Pointers:

The FFI::MemoryPointer class allocates native memory with automatic garbage collection as a sweetener. When a MemoryPointer goes out of scope, the memory is freed up as part of the garbage collection process.

So you shouldn't have to call pointer.free directly. Also, just to check whether you had to manually free result_pointer, I called result_pointer.free after printing extracting the array and got this warning

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

So it looks like you don't have to manually free result_pointer either.

like image 163
Sandy Avatar answered Sep 22 '22 04:09

Sandy