Foreign function interface (FFI)

FFI is used to allow a program written in one language invoke functions written in another.

In this example I wrote a small c program that we are going to invoke from a ruby program.

static const char* alias_name = "ALTER_EGO";

char* greet(char* name) 
{
  char* alias = getenv(alias_name);
  char* buffer = (char*)malloc(128);
  snprintf(buffer, 128, "Hello %s!\n", alias == NULL ? name : alias);
  return buffer;
}

From the ruby side of the world we can use ‘ffi’ to load our c program. First we will need to compile our shared object (.so) file so that we can access it.

$ gcc -shared -o libgreeting.so -fPIC greeting.c

Now we can use FFI to define the interface of the c library function that we wish to invoke.

require 'ffi'

class Greeting
  extend FFI::Library
  ffi_lib 'greeting'
  attach_function :greet, [:string], :string
end

puts Greeting.greet('mo')

The ffi_lib call loads the libgreeting.so file. attach_function defines a function called greet that accepts a string as an input argument and returns a string.

Now when we run this:

λ rake run
rm -f *.so
gcc -shared -o libgreeting.so -fPIC greeting.c
LD_LIBRARY_PATH=. ruby greeting.rb
Hello mo!

We can also run this with environment variable loaded.

λ ALTER_EGO=slim rake run
  rm -f *.so
  gcc -shared -o libgreeting.so -fPIC greeting.c
  LD_LIBRARY_PATH=. ruby greeting.rb
  Hello slim!

The full source code for this can be found on github.

comments powered by Disqus