Ruby’s New FFI Library: Calling External Libraries Just Got A Whole Lot Easier
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!
November 1, 2008 at 7:03 pm
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.
November 1, 2008 at 9:08 pm
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.
November 3, 2008 at 1:52 pm
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.
November 4, 2008 at 6:46 am
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!
November 5, 2008 at 12:16 pm
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.
November 5, 2008 at 3:05 pm
Doesn't build on Windows. :(