How to Write a Ruby C Extension (Step-by-Step)

Writing a C extension allows you to interact with Ruby from C.

You may want to this if there is a particular important method you would like to optimize with the speed of C, or if you would like to create an interface between a C library and Ruby.

An alternative would be to use the FFI module.

Now:

Let’s discover how to write a C extension!

Your First C Extension

Create a file named extconf.rb with these contents:

require 'mkmf'

create_header
create_makefile 'foobar'

Then run ruby extconf.rb which will create a few files for you (Makefile & extconf.h). Don’t change these files.

Now create foobar.c, this name comes from the create_makefile line in extconf.rb. You can change it if you want but it has to match your .c filename for this to work.

Inside foobar.c you add this:

#include "ruby.h"
#include "extconf.h"

void Init_foobar()
{
  // Your C code goes here
}

This is the basic skeleton of your C extension. Notice that you have to use the name you declared on extconf.rb plus the word Init_. In this case Init_foobar.

You can compile this by running make, and what you get is a so file which you can load into your Ruby application using require.

Writing A Ruby Method From C

You can create c functions that you can call from your Ruby code. This is how built-in classes & methods work.

All the Ruby methods have to return a VALUE. A VALUE is a Ruby object.

Example:

VALUE rb_return_nil() {
  return Qnil;
}

Now we need to attach this function to a Ruby class or module. You can create a new class or use an existing one.

Here I’m creating a module:

void Init_foobar() {
  VALUE mod = rb_define_module("RubyGuides");

  rb_define_method(mod, "return_nil", rb_return_nil, 0);
}

You can use the rb_define_method method to attach the C function to this module.

The arguments are:

Argument Description
mod Module object
“print_hello” Name of the Ruby method
rb_print_hello Name of the C function
0 Number of arguments this method takes, use -1 for variable arguments

Now run make again to compile the extension, and try it like this:

require 'foobar'

include RubyGuides
print_hello

There you go, you just called a C function from Ruby!

Creating a Ruby Hash From C

Often you will want to create & manipulate Ruby objects from C.

You can do that using the C API.

For example, to create a hash & add some elements to it:

VALUE create_hash() {
  hash = rb_hash_new();

  rb_hash_aset(hash, rb_str_new2("test"), INT2FIX(1));
  return hash;
}

Two important things to notice here, first the rb_str_new2 function, this creates a Ruby string.

Then the INT2FIX macro, this converts a regular integer into a Ruby integer.

Now if you expose this function to Ruby using rb_define_method you will get a regular Ruby hash with one key (test) & a value of 1.

Don’t forget to recompile your extension every time you make changes 🙂

Working With Strings

Your C functions can also take parameters.

For example:

VALUE rb_print_length(VALUE self, VALUE str) {
  if (RB_TYPE_P(str, T_STRING) == 1) {
    return rb_sprintf("String length: %ld", RSTRING_LEN(str));
  }

  return Qnil;
}

void Init_foobar()
{
  rb_define_global_function("print_length", rb_print_length, 1);
}

Here we are taking a VALUE (ruby object) as an argument & then we make sure it’s a string with the RB_TYPE_P macro. After that, we just print the string length using the RSTRING_LEN macro.

If you want a pointer to the string data you can use the RSTRING_PTR macro.

Example:

VALUE rb_print_data(VALUE self, VALUE str) {
  if (RB_TYPE_P(str, T_STRING) == 1) {
    return rb_sprintf("String length: %s", RSTRING_LEN(str));
  }

  return Qnil;
}

Also notice how there is another parameter here, self, this is the object receiving the method call.

If you want to define this method on the String class itself you would do it like this:

void Init_foobar()
{
  rb_define_method(rb_cString, "print_length", rb_print_length, 0);
}

Summary

You learned how to write a C extension for Ruby so you can access all the power of C from your Ruby programs. You also learned how to expose C functions into Ruby & how to work with Ruby objects from C, like strings & hashes.

If you need more functions to interact with Ruby from C you may have to find them in Ruby’s source code & reading C extension code since this is not well documented.

Hope you found this article useful & interesting! If you did please share it with your friends so they can enjoy it too.