Extending Ruby with Extensions, FFI or Inline
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:
require 'mkmf' dir_config('helloext') create_makefile('helloext')
Also, helloext.c contains this:
#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:
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:
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
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.
Latest Articles by Our Team
Our expert team of designers and developers love what the do and enjoy sharing their knowledge with the world.
-
Is it worth upgrading your Rails application?
-
The Axioms of Software Development - Part 4
-
No app left behind: Upgrade your application to Ruby 3.0 and s...
We Hire Only the Best
reinteractive is Australia’s largest dedicated Ruby on Rails development company. We don’t cut corners and we know what we are doing.
We are an organisation made up of amazing individuals and we take pride in our team. We are 100% remote work enabling us to choose the best talent no matter which part of the country they live in. reinteractive is dedicated to making it a great place for any developer to work.
Free Community Workshops
We created the Ruby on Rails InstallFest and Ruby on Rails Development Hub to help introduce new people to software development and to help existing developers hone their skills. These workshops provide invaluable mentorship to train developers, addressing key skills shortages in the industry. Software development is a great career choice for all ages and these events help you get started and skilled up.