ProjectIdeas/RubyOCaml

Example OCaml/Ruby extension Library Instructions

Recently there has been quite a bit of discussion on comp.lang.ruby about using OCaml libraries with Ruby. This would provide a way to use a "safe" garbage collected language that is almost as fast as native C to write extension libraries for Ruby.

Disclaimer: This is prototype code only! Much work would need to be done in order for this to be generally useful. It just proves that OCaml libraries for Ruby are possible.

Teaching OCaml is beyond the scope of this article. For more information on OCaml, visit [WWW] http://caml.inria.fr/.

Information on creating C callable OCaml libraries can be found at [WWW] http://caml.inria.fr/pub/docs/manual-ocaml/manual032.html.

This method was tested on a Sun Ultra 10 running Solaris 10, Ruby 1.8.2 and OCaml 3.08.2. YMMV with another OS/Ruby/OCaml version.

Creating the OCaml code

First we need to create the OCaml library code that we want to call from Ruby. To make things easy, we'll just use a variation of the fibonacci code from INRIA's "Advanced topic: callbacks from C to Caml", section 18.7.

(*  OCaml to Ruby test  *)

let rec fib n = 
    if n < 2 then 1
      else (fib (n-1)) + (fib(n-2));;

(*  C interface callback  *)

let _ = Callback.register "fib" fib;;

Download attachment fib.ml

The important piece in our library code is the Callback.register function. This registers a global callback for a given closure that can be accessed from C.

Wrapping the OCaml code as a C library

Next we need to create a C wrapper. We could probably combine this code with the next step but since the OCaml documentation shows this code being compiled with ocamlc or ocamlopt instead of directly with the C compiler, we'll leave it as an independent step for now.

/*  C wrapper for OCaml library  */

#include <stdio.h>
#include <string.h>
#include <caml/mlvalues.h>
#include <caml/callback.h>

int fib(int n)
{
        static value * fib_closure = NULL;
        if (fib_closure == NULL) fib_closure = caml_named_value("fib");
        return Int_val(callback(*fib_closure, Val_int(n)));
}

Download attachment fibwrap.c

Compiling the OCaml code and the C wrapper

At this point you can choose to either compile the OCaml code to byte code or to native code.

The native code option is the only one currently tested.

For byte code, do this:

ocamlc -custom -output-obj -o fibcaml.o fib.ml
ocamlc -c fibwrap.c
cp /usr/local/lib/ocaml/libcamlrun.a libfib.a
ar r libfib.a fibcaml.o fibwrap.o

For native code, do this:

ocamlopt -output-obj -o fibcaml.o fib.ml
ocamlopt -c fibwrap.c
cp /usr/local/lib/ocaml/libasmrun.a libfib.a
ar r libfib.a fibcaml.o fibwrap.o

Now you should be ready for the Ruby stuff!

The Ruby/C wrapper

The C code for the Ruby/OCaml extension isn't really any different from any Ruby C extension other than having to initialize the OCaml runtime before allowing an object to be created.

#include "ruby.h"

VALUE cfib;

/*  Class initialization routine called when a object
 *  is created.
 *  Put any class initialization here
 */

static VALUE fib_init(VALUE self)
{
        return self;
}

/*  Calls the C fib wrapper function for OCaml, translating
 *  between Ruby objects and C data types.
 */

static VALUE fib_fib(VALUE self, VALUE num)
{
        return INT2NUM(fib(NUM2INT(num)));
}

/*  This will be the **argv array that is passed to the
 *  OCaml initialization routine.
 *  Any special command-line args should go here.
 */

char *argv[] = {
        "fibtest",
        NULL
};

/*  Standard Ruby module initialization code...
 *  The only thing special is initializing the
 *  OCaml runtime.
 *
 *  A robust implementation would check to see if
 *  OCaml is already loaded and not load it.
 *
 *  This would be necessary if multiple OCaml libraries
 *  were going to be loaded and you only want one
 *  instance of the OCaml runtime.
 */

void Init_fib()
{
        /*  Initialize the OCaml runtime  */

        caml_startup(argv);

        /*  Initialize the Ruby Fib class  */

        cfib = rb_define_class("Fib",rb_cObject);
        rb_define_method(cfib,"initialize",fib_init,0);
        rb_define_method(cfib,"fib",fib_fib,1);
}

Download attachment fibruby.c

Create extconf.rb

Next we have to create the extconf.rb file which we'll use to create the Makefile for the Ruby extension. This is standard Ruby extension stuff.

require 'mkmf'
have_library("fib","fib")
create_makefile("fib")

Download attachment extconf.rb

Simply execute the extconf.rb file to create the Makefile

Assuming ruby is in your path:

ruby extconf.rb

Then make the extension:

make

This will create a shared library, assuming that your system supports shared libraries.

Testing our new OCaml/Ruby library

You can test the library by downloading the following example script and executing it.

#  Ruby/OCaml test

require 'fib'

test = Fib.new
print "test.fib(10) = #{test.fib(10)}\n"

Download attachment testfib.rb

Congradulations! You have just made your first OCaml/Ruby extension library.

Download all the source for this project ruby-ocaml.tar.gz

All the files for this page can be found here

Where to go from here

This can obviously be improved quite a bit. Is anybody interested?

(from cilibrar) I think this looks like a job for a code generator. To automatically generate all that glue code.