This first part aims at explaining how Ruby in the background is not Ruby anymore. In the next part I’m going to talk about HOW to bridge some existing C library with your ruby application.
As you might know already, the official ruby interpreter is written in pure C. This means that whenever you type a ruby instruction, the interpreter will call the corresponding C function for you. For example, if you do :
arr = Array.new
Ruby will call a C function (probably rb_ary_new) and a C structure will be created to hold the content of arr. When you will try to access arr later in your code, the interpreter will use that same C structure.
On the ruby side of things we say that everything is an object. On the C side of things we say that everything is a VALUE
You also already know that everything in ruby is an object. To represent this reality on the C side of things, a custom type (typedef) has been created and has been given the name of VALUE. This type is a pointer (well, in fact it is not really a pointer but we won’t go into this right now) that can reference any kind of data type. When C defines something as a VALUE, you know that it is dealing with something that either A) comes from the ruby side of things, B) will be returned to the ruby side of things or C) is usable on the ruby side of things in one way or another. In all other situations, C is just C and doesn’t need the VALUE data type. When you see C using VALUE, what you are really seeing is ruby from the inside, and ruby from the inside is an ugly beast. It is not pretty, not elegant, not easy, not cool and honestly not that interesting. The thing is, it can save you a lot of work if you want to bridge an existing C library into your ruby application instead of having to rewrite the whole thing in plain ruby. That, in part, is why it can be very useful to know a little bit of ruby’s ugly side.
Let’s move on. We said that VALUE was the C way to represent a ruby object. If you open one of the source file of the ruby interpreter (array.c, string.c, etc.), you will find that the word VALUE is used everywhere :
VALUE rb_do_stuff(VALUE param1, VALUE param2) {
VALUE some_variable;
int x;
//bla bla bla bla
return some_variable;
}
Since everything in ruby is an object (or a VALUE), the C functions that are responsible of returning something ready to use on the ruby side of things always have to return a VALUE. If they would return something else (int, char, char *, long), ruby would complain because it can understand objects and only objects.
A Fixnum is not a int, it is a VALUE. A Bignum is not a long, it is a VALUE. A String is not a char *, it is a VALUE, and so on.
VALUE is not really a pointer, you say?
Ok the following is not really important for the sake of this post but if you’re curious it will interest you. I said earlier that VALUE wasn’t really a pointer. Time has come to explain what it is and why. VALUE is in fact an unsigned long.
There is only one reason why VALUE is an unsigned long and not a pointer : efficiency.
Remember what a pointer is? It is a variable that contains an address to some structure in memory. It is perfect for complex objects like arrays, hashes and custom objects… but it is a bit overkill for more primitive objects like Fixnums, booleans (TrueClass and FalseClass instances) and nil (NilClass instance). Ruby inventor thought that it would be great if in some cases the VALUE object could BE the data instead of POINTING TO the data. I mean, why having a VALUE that points to a structure that contains an integer that contains the number 1 when you could simply have a VALUE that contains the number 1?
So instead of being a pointer, VALUE is an unsigned long that can contain either A) an address to some structure that contains the data or B) the data itself (immediate value).
The data in a VALUE is stored in such a way that it is possible to know if it contains an address to some structure in memory or an immediate value. Ingenious, ain’t it?
In my next post, I will try to explain how you can use the ugly side of ruby to bridge an existing C library into your ruby (or rubyonrails) application.