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.