How to create a Ruby extension in C in under 5 minutes
Many coders will reach a situation where developing a C extension makes sense, whether for doing 'heavy lifting', diving into assembly language, interfacing with other C code, etc. Luckily, developing a basic Ruby extension in C is easy.
Note: This article assumes you are using a UNIX of some sort (this was all tested on OS X) and that you have Ruby installed properly (from source, ideally, so you have ruby.h available). If not, you may be stuck.
First, create a directory called MyTest (or whatever you want your extension to be called) and in there create two files, extconf.rb and MyTest.c (if you want to download pre-written sources, they're in this tar file). In extconf.rb, put the following:
# Loads mkmf which is used to make makefiles for Ruby extensions require 'mkmf' # Give it a name extension_name = 'mytest' # The destination dir_config(extension_name) # Do the work create_makefile(extension_name)
This code is pretty self descriptive. It loads up Ruby's makefile library, sets up the environment, and creates a Makefile. Next, create the actual extension in MyTest.c. Here's some demonstration code to create a basic module with a single method called 'test1' which returns '10' when called:
// Include the Ruby headers and goodies #include "ruby.h" // Defining a space for information and references about the module to be stored internally VALUE MyTest = Qnil; // Prototype for the initialization method - Ruby calls this, not you void Init_mytest(); // Prototype for our method 'test1' - methods are prefixed by 'method_' here VALUE method_test1(VALUE self); // The initialization method for this module void Init_mytest() { MyTest = rb_define_module("MyTest"); rb_define_method(MyTest, "test1", method_test1, 0); } // Our 'test1' method.. it simply returns a value of '10' for now. VALUE method_test1(VALUE self) { int x = 10; return INT2NUM(x); }
For C, it's reasonably simple code. We include the Ruby headers via ruby.h, set up a variable to store the module in, and create two functions, one which is called by Ruby when it initiates the module, and the other is our test1 method (Note that in the code above we define MyTest as a module, but you could just as easily use rb_define_class to create a class, if that's what you wanted to do.)
Next we need to compile our hard work. Make sure you're in the MyTest directory and run ruby extconf.rb, and it should say that the Makefile has been created. If so, you can then run make to compile and build the extension. As long as no errors occur, run up irb (or create a new Ruby program) and test out your newly build extension like so:
irb(main):001:0> require 'mytest' => true irb(main):002:0> include MyTest => Object irb(main):003:0> puts test1 10
Et voila! The world's simplest Ruby extension written in C in under 5 minutes. It doesn't do much, it's overly basic, but this is the springboard to greater things :)
August 1, 2006 at 7:36 pm
It should be noted that 5 minutes is glacial compared to what you can do with inline (and how much easier it is). The above example was done (and ran) in less than 30 seconds!
require 'rubygems'
require 'inline'
class Example
inline(:C) do |builder|
builder.c "int method_test1() {
int x = 10;
return x;
}"
end
end
p Example.new.method_test1
Please take notice of a couple things. 1) you just write it and run it. This is important. No extra development cycles are needed. I realize that your mkmf example is pretty straight forward, but there is a world of difference between having to think about what you need to do next vs just running your script and having it taken care of automatically. That includes compiler/linker flag changes, missing a make, etc etc... 2) notice I just say int and return x. Type conversions are taken care for you. 3) the resultant code looks almost exactly like yours, but with 1/10th the effort and time.
As an aside, your MyTest should probably be made static, or better, just local to your Init. You can also avoid all your prototypes if you just reorder your two functions.
August 1, 2006 at 7:37 pm
that looked prettier in the comment box. :/
August 1, 2006 at 7:49 pm
Thanks for the example. RubyInline has been covered here, btw (and at InfoQ), and I'm keeping an eye on developments, it looks like it's going great. Well done!
I like these type conversions..
August 2, 2006 at 8:18 am
"require 'inline'
LoadError: no such file to load -- inline"
How can i get the module 'inline'?
Thanks
August 2, 2006 at 11:25 am
gem install rubyinline or gem install RubyInline .. I forget :)
August 2, 2006 at 5:50 pm
Windows gets no love here. I think you have ot have Visual C++ 6.0 installed to use these tools. Ugh.
August 2, 2006 at 6:41 pm
awesome sample. thanks zenspider and peter.
Pingback: Anonymous
August 2, 2006 at 8:00 pm
To Just and Windows Users:
It tried this under Cygwin and it worked fine. Don't bother with Visual C++ 6.0. Cygwin is free and wonderful.
August 2, 2006 at 10:43 pm
What I'd rather see is a RubyInline example that interacts with arrays. It's pretty rare that operating on a single value is the slow thing I want to extend; and pretty common that processing large arrays sucks.
Anyone have that kind of 5-minute example?
August 2, 2006 at 10:53 pm
sooo much easier with SWIG: http://www.swig.org
August 11, 2006 at 11:04 pm
Hmm..using Cygwin (just installed).
I get a Makefile:104: *** target pattern contains no '%'. Stop.
error...any ideas?
August 25, 2006 at 8:46 pm
Me too peppermonkey - maddening! did you find the solution?