How-To: A wrapper to ruby for a simple C dynamic library named libhello.so

One rainy summer evening, I wondered about that sacred secret, which is how ruby can work with C or C++ libraries.
Suddenly, I felt very interested on learning how to program it, since my faith issues and sex of angels discussions were completely exhausted.
In front of my upgraded Debian SID of 25-may-2007, I checked the presence of debian packages gcc, gcc-doc, rubybook, libruby, and ruby1.8-dev.
I was utterly ready for starting to code.
In this how-to, I'll:
1. Create a .so library, libhello.so
2. Test the previous one, use_hello
3. Create a wrapper for ruby, hello.so
4. Use the wrapper by requiring it from helloer.rb
1. Create a .so library, libhello.so
First I coded the simple library:
Thanks to file:///usr/share/doc/gcc-4.1-doc/gcc.html#Link-Options, I know that:
andandare@debian:~/Desktop/coding/ruby/c4ruby/hello$ cat libhello.c
#include "libhello.h"
void hello() {
printf ("hello chap!\n");
}
andandare@debian:~/Desktop/coding/ruby/c4ruby/hello$ cat libhello.h
#include <stdio.h>
void hello();
-fpic
Generate position-independent code (PIC) suitable for use in a shared library, if supported for the target machine. Such code accesses all constant addresses through a global offset table (GOT). The dynamic loader resolves the GOT entries when the program starts (the dynamic loader is not part of GCC; it is part of the operating system). If the GOT size for the linked executable exceeds a machine-specific maximum size, you get an error message from the linker indicating that -fpic does not work; in that case, recompile with -fPIC instead. (These maximums are 8k on the SPARC and 32k on the m68k and RS/6000. The 386 has no such limit.)
Position-independent code requires special support, and therefore works only on certain machines. For the 386, GCC supports PIC for System V but not for the Sun 386i. Code generated for the IBM RS/6000 is always position-independent.
... and ...
-shared
Produce a shared object which can then be linked with other objects to form an executable. Not all systems support this option. For predictable results, you must also specify the same set of options that were used to generate code (-fpic, -fPIC, or model suboptions) when you specify this option.
So, I just compiled!
andandare@debian:/home/common/Desktop/coding/ruby/c4ruby/hello$ gcc -fpic -shared libhello.c -o libhello.so
2. Test the previous one, use_hello
I wanted to be sure that libhello.so was a dynamic library. So, I decided to do a simple C program, just to test that libhello.so works.
andandare@debian:/home/common/Desktop/coding/ruby/c4ruby/hello$ cat use_hello.c
#include "./libhello.h"
int main()
{
hello();
return 0;
}
andandare@debian:/home/common/Desktop/coding/ruby/c4ruby/hello$ gcc ./libhello.so use_hello.c -o use_hello
andandare@debian:/home/common/Desktop/coding/ruby/c4ruby/hello$ ./use_hello
hello chap!
I erased the -supposed to be- dynamic library.
andandare@debian:/home/common/Desktop/coding/ruby/c4ruby/hello$ rm libhello.so
andandare@debian:/home/common/Desktop/coding/ruby/c4ruby/hello$ ./use_hello
./use_hello: error while loading shared libraries: ./libhello.so: cannot open shared object file: No such file or directory
After proving that libhello.so is actually dynamic, I restored the library
andandare@debian:/home/common/Desktop/coding/ruby/c4ruby/hello$ gcc ./libhello.so use_hello.c -o use_hello
3. Create a wrapper for ruby, hello.so
It is just a library that uses ruby.h and wraps the most interesting functions of libhello.
By the way, there are not many choices.
andandare@debian:/home/common/Desktop/coding/ruby/c4ruby/hello$ cat hello.c
#include "/usr/lib/ruby/1.8/i486-linux/ruby.h"
#include "libhello.h"
static VALUE t_init(VALUE self)
{
return self;
}
static VALUE t_hello(VALUE self)
{
hello();
return Qnil;
}
VALUE cHelloer;
void Init_hello() {
cHelloer = rb_define_class("Hello", rb_cObject);
rb_define_method(cHelloer, "initialize", t_init, 0);
rb_define_method(cHelloer, "hello", t_hello, 0);
}
Thanks to file:///usr/share/doc/rubybook/html/ext_ruby.html, I know that I need to declare always a first parameter, which is called self, into t_init and t_hello.
Such parameter is the own object. Additional parameters would be ruby method's parameters.
Every method in hello.c, but Init_hello, has to return a ruby value, even when VALUE is nil.
The Hello class is very simple and we don't need to declare any attribute inside.
Init_hello allows to declare the new class Hello: "cHelloer = rb_define_class("Hello", rb_cObject);".
Here, cHelloer is the class "Hello" and another mere ruby object, which inherits from the ruby class named Object.
In a ruby script, Hello.new will call the function t_init.
Then, I declared the method hello, that calls to the function t_hello and receives zero parameters, with the line:
Similarly to libhello.so and use_hello, I compiled the dynamic library that wraps libhello C library.
rb_define_method(cHelloer, "hello", t_hello, 0);
andandare@debian:/home/common/Desktop/coding/ruby/c4ruby/hello$ gcc -fpic -shared ./libhello.so hello.c -o hello.so
4. Use the wrapper by requiring it from helloer.rb
I wrote a more simple code in helloer.rb,
and everything worked fine and I was very happy, after executing
andandare@debian:/home/common/Desktop/coding/ruby/c4ruby/hello$ cat helloer.rb
# Load the interface to the C shared library
require 'hello'
helloer = Hello.new
(1..4).each do helloer.hello end
andandare@debian:/home/common/Desktop/coding/ruby/c4ruby/hello$ ruby helloer.rb
I love successful plans. See you chap!.
hello chap!
hello chap!
hello chap!
hello chap!