Mathias Jean Johansen

Ruby Extensions in C

Occasionally, we come across particular sections in our programs that need to be exceptionally fast. Ruby allows us to write extensions in C, so that we can delegate the heavy lifting. In this post, I’ll show you how easy it is to extend Ruby with C by writing a trivial factorial function in C which we’ll be able to call from Ruby.

We start out by creating the relevant directories and files.

$ mkdir fact
$ cd fact
$ mkdir ext
$ touch ext/extconf.rb ext/fact.c

In the ext/extconf.rb file, we require the mkmf module which allows us to generate an applicable Makefile that compiles our C code.

require "mkmf"

create_makefile("fact")

Afterwards, we write the actual C program in the ext/fact.c file. It should look like this:

#include "ruby.h"

void Init_fact();
VALUE fact(VALUE self, VALUE n);

void Init_fact()
{
  VALUE Fact = rb_define_module("Fact");
  rb_define_method(Fact, "fact", fact, 1);
}

VALUE fact(VALUE self, VALUE n)
{
  int x = NUM2INT(n);
  int factorial = 1;

  for (int i = 1; i <= x; i++) {
    factorial *= i;
  }

  return INT2NUM(factorial);
}

First, we include the ruby.h header file, so that we can access the necessary macros and functions. Ruby will by default execute our initializing function Init_fact, so we define our Fact module in here with the function rb_define_module. Additionally, we define a method fact in the Fact module with the rb_define_method function. It takes a class, a method name, a function and the number of arguments.

In the function VALUE fact(VALUE self, VALUE n), we pass both VALUE self and the number we want to take the factorial of. We convert the VALUE n to an int with the NUM2INT macro, and convert the integer back to Fixnum when returning the result.

We can then compile our program, and open irb to verify that it works as expected:

$ ruby ext/extconf.rb
creating Makefile
$ make
compiling ext/fact.c
linking shared-object fact.bundle
$ irb
irb(main):001:0> require_relative "fact"
=> true
irb(main):002:0> include Fact
=> Object
irb(main):003:0> fact(5)
=> 120

Utilizing C in our Ruby programs can be incredibly useful both in order to achieve higher performance for specific parts of our application, but also if we want interfacing with other C code. If you want to go further, I highly recommend that you take a look at the README.EXT documentation.