12/08/2018, 14:30

Cross language interfacing: Ruby to C

Majority number of tasks can be accomplished these days using one of the many available dynamic languages. But, some non-trivial task (performance critical developments like cryptographic algorithm, game engine, driver) requires compiled languages. Fine-grained control over low-level interfaces is ...

Majority number of tasks can be accomplished these days using one of the many available dynamic languages. But, some non-trivial task (performance critical developments like cryptographic algorithm, game engine, driver) requires compiled languages. Fine-grained control over low-level interfaces is also another reason. Ruby is one of the most popular dynamic languages, the popularity of which, is mostly web development centric. But, it's rich feature-set makes it equally usable to other kind of developments too. Considering an application that requires a lot of processing power, can not be served well using procedural Ruby (cause there's too much processing overhead that the language itself will generate). But, if it is interfaced with C, we can expose our very dynamic Ruby application to the performance centric world of C.

Setup

Let's create a project directory,

$ mkdir ruby_c
$ cd ruby_c

Now, create a configuration file for our C extension.

touch extconf.rb

and put the following lines in it.

require 'mkmf'

create_header
create_makefile 'c_math'

We are using makefile for our extension and the ruby module mkmf generates it for us. create_header creates extconf.h, and like the syntax suggests, create_makefile 'c_math' creates makefile Makefile.

Now, running the extconf.rb will generate makefile and header. And, it is ready to compile. Compilation is performed using make command.

$ ruby extconf.rb
$ make

But, this actually does nothing, since we don't have the actual C extension yet. Let's create it.

touch c_math.c

And put in,

#include <ruby.h>
#include "extconf.h"

void Init_c_math() {
}

This file is the entry point of our C extension. The odd looking method Init_c_math is called when the extension is required.

Example 1

#include <ruby.h>
#include <time.h>
#include <stdlib.h>
#include "extconf.h"

VALUE random_number(VALUE);

void Init_c_math()
{
  VALUE CMath;

  CMath = rb_define_class("CMath", rb_cObject);
  rb_define_method(CMath, "random_number", random_number, 0);
}

VALUE random_number(VALUE self) {
  srand(time(NULL));
  return rand();
}

In this example we created a class CMath containing a method random_number, in our extension.

We declared a prototype for our method of the CMath class,

VALUE random_number(VALUE);

Then, declared the method,

VALUE random_number(VALUE self) {
  srand(time(NULL));
  return rand();
}

which is nothing special but a random number generator. And, we make our extension entry-point Init_c_math aware of the existence of a class

CMath = rb_define_class("CMath", rb_cObject);

and a method,

rb_define_method(CMath, "random_number", random_number, 0);

in our extension.

The class definition is provided by rb_define_class which accepts the class name, and class as a parameter. And, rb_define_method defines method which accepts container class, method name, method and number of arguments respectively.

Pretty easy, nah? Let's see another example,

Example 2

#include <ruby.h>
#include <time.h>
#include <stdlib.h>
#include "extconf.h"

VALUE square(VALUE, VALUE);

void Init_c_math()
{
  VALUE CMath;

  CMath = rb_define_class("CMath", rb_cObject);
  rb_define_method(CMath, "square", square, 1);
}

VALUE square(VALUE self, VALUE number) {
  long translated_number;
  translated_number = FIX2LONG(number);
  return LONG2FIX(translated_number * translated_number);
}

This is the same class as before with a different math method square which accepts a number and returns the squared value (as always             </div>
            
            <div class=

0