Ruby Weekly is a weekly newsletter covering the latest Ruby and Rails news.

Ruby’s New FFI Library: Calling External Libraries Just Got A Whole Lot Easier

By Peter Cooper / November 1, 2008

ffi-useful.png Charles Nutter, of the core JRuby team, writes:

One of the largest problems plaguing Ruby implementations [..] is the ever-painful story of "extensions". In general, these take the form of a dynamic library, usually written in C, that plugs into and calls Ruby's native API as exposed through ruby.h and libruby.

The many compiled bridges between external libraries and Ruby pose a problem for alternate implementations like JRuby, because of the complexity involved in exposing internals of the implementation or expensive serialization in both directions. Instead, an interface is necessary so that instead of developing libraries that act only as unique bridges to others, we can just have one library that provides the interface to any arbitrary library of our choice.

Ruby already has a library called "dl" that makes it possible to dynamically link external libraries with Ruby. It's a bit arcane though, and Charles points out that it's not widely used "because of real or perceived bugs." Given this, and given the need for an implementation that can be compatible with JRuby, Wayne Meissner has developed "FFI", a new Ruby library that provides "Foreign Function Interface" features to Ruby.

A basic demonstration of a Ruby script that uses C's getpid function should be enough to demonstrate the simplicity of FFI:

require 'ffi'

module GetPid
  extend FFI::Library
  attach_function :getpid, [], :uint
end

puts GetPid.getpid

The best part? FFI is available for Ruby 1.9, Ruby 1.8.6 and 1.8.7 (just gem install ffi), as well as JRuby 1.1.4 (and above) and Rubinius (Rubinius's implementation is separate to Wayne's, but works similarly). Wayne has also written a blog post with his side of the story. He says he's waiting to "see how people are actually using it in the real world."

This FFI implementation has the hallmarks of becoming a new Ruby standard for calling out to external, compiled libraries. Let's get behind it, start using it, and crank out some code to see how it operates!

Comments

  1. Jackson says:

    This seems a little strange to me, because Ruby's extension bindings are already great. But I think Python has a similar thing. When you don't have to modify the behavior if the external library at all, it makes sense.

  2. Eero Saynatkari says:

    Jackson, I think the primary benefit is simply avoiding writing an extension which requires one to be comfortable writing the necessary C glue code--and be able to compile it! Using FFI is probably far less daunting for many users.

    An extension also requires wrapping any foreign functions so that Ruby can access them, which typically leads to an unnecessary explosion of C code ("well, maybe I should just do the strcmp() here since I already started..") The FFI layer encourages the user to stay in Ruby except for what is absolutely needed.

  3. Beoran says:

    I think this is nice because it's portable between the different versions of Ruby: JRUBY, MRI and Rubinius.

    However, in C Ruby you can already use dl, and that's arguably even easier in Ruby 1.9.1. Here's the same example with dl :

    require 'dl'
    module GetPid
    extend DL::Importer
    dlload "libc.so.6"
    extern "unsigned int getpid()"
    end

    puts GetPid.getpid

    But, then again, dl seems to be significantly slower than ffi.

  4. Leon Bogaert says:

    This seems very nice! I'm no expert at c and this seems like a nice way to use all those c libraries! I think this could be a big step forward for ruby!

  5. Jan Wedekind says:

    A standardized API for writing extensions certainly would help competing Ruby implementations. However I think there are major practical problems with this
    * Platform-dependent type definitions are not accessible. For example function headers are usually partially different on 32-bit and 64-bit platforms.
    * C++ libraries (especially those making use of templates) cannot be used like this.
    * Inline functions, preprocessor macros, and composite types need to be duplicated by the Ruby extension.
    However on the long term this may still be the best way to develop Ruby extensions. A Ruby VM can only optimise the code of the extension if the Ruby extension is largely written in Ruby itself.

  6. Daniel Berger says:

    Doesn't build on Windows. :(

Other Posts to Enjoy

Twitter Mentions