Blog

Extending Ruby with Extensions, FFI or Inline

Placeholder Avatar
Yuji Yokoo
September 14, 2015

Introduction

Ruby is a great programming language, but like all programming languages it is not suitable for everything. Sometimes it can make sense to use native libraries on the platform or C to improve the performance of slow Ruby code.

This post will explore calling C libraries and functions from Ruby. Although the methods mentioned in this post are not limited to just that, it is a very common use case. Ruby has a few ways of integrating with native code. This post will look at native extensions, foreign function interface, and inline.

Native Extensions

Native extensions provide a method of interfacing with C code written in a certain way to make it accessible from Ruby. Typically, extensions are written in C, then built, loaded and called from Ruby. This is commonly used to interact with platform libraries by providing a wrapper or an additional API layer for them. RMagick and nokogiri are among the most popular gems that use extensions. The notable disadvantages of this approach are that it is tightly coupled with the Ruby VM implementation, the extension must be built when installing or updating, and also it is in C. Typically, a Ruby script is used to produce a Makefile, and the Makefile is then used to build the extension.

Extensions example

In a trivial example, there are a couple of files:

$ ls extconf.rb helloext.c extconf.rb contains this: ruby require 'mkmf' dir_config('helloext') create_makefile('helloext') Also, helloext.c contains this: c #include "ruby.h" VALUE hello_ext(VALUE self) { printf("Hello, extensions!\n"); return Qnil; } void Init_helloext() { rb_define_global_function("hello_extensions", hello_ext, 0); } Building and execution goes like this: $ ruby extconf.rb creating Makefile $ make compiling helloext.c linking shared-object helloext.bundle $ ls Makefile extconf.rb helloext.bundle helloext.c helloext.o

This extension can be loaded and utilised from Ruby code: $ irb 2.1.2 :001 > require './helloext.bundle' => true 2.1.2 :002 > hello_extensions Hello, extensions! => nil

Foreign Function Interface

The Foreign Function Interface (FFI) is another method of interfacing with native binaries. It utilises libffi installed on the platform, and does not require compilation. It is very simple to use this for loading a platform library and calling its functions. There are two common ways of using the FFI in Ruby; the ffi gem and Fiddle. Fiddle is part of the standard library and ffi is a gem. While the ffi gem has a wiki with lots of information, the Fiddle documentation is not extensive.

The main advantage of this approach is that it is cross-platform and not limited to MRI, since it is not tightly coupled with the Ruby implementation. Another advantage is that there is no need to build binaries once FFI is set up and functioning.

FFI example

The GitHub page for ffi contains this example: ruby require 'ffi' module MyLib extend FFI::Library ffi_lib 'c' attach_function :puts, [ :string ], :int end MyLib.puts 'Hello, World using libc!' An example with Fiddle which does the same might look like this: ruby require 'fiddle' libc = Fiddle.dlopen('/usr/lib/libc.dylib') libc_puts = Fiddle::Function.new(libc['puts'], [Fiddle::TYPE_VOIDP], Fiddle::TYPE_INT) libc_puts.call("Hello, Fiddle!") As they do not require compilation, they can be run directly like this: $ ruby ffi-example.rb Hello, World using libc! $ ruby fiddle-example.rb Hello, Fiddle!

Inline

RubyInline is a gem that makes it possible to embed “inline” C code in Ruby. Typically C code is embedded in string literals and built at runtime. The notable advantage is that it is trivially simple to embed a little C code with RubyInline. The main disadvantages are that it requires compilation at runtime, and that code embedded in strings can be hard to work with.

RubyInline example

ruby require "inline" class HelloInline inline do |hello| hello.c ' void hello_inline() { printf("Hello, RubyInline!\n"); } ' end end HelloInline.new.hello_inline RubyInline takes care of building the binary, so this can be called directly too. $ ruby inline.rb Hello, RubyInline!

Final Thoughts

Although I love how easy it is to embed a small C function with RubyInline, I would not like to embed C code in strings, or to compile it at runtime. Previously I have only used extensions, but after writing this post, I would probably use ffi over extensions.