This post originated from an RSS feed registered with Ruby Buzz
by Daniel Berger.
Original Post: Type checks in C extensions
Feed Title: Testing 1,2,3...
Feed URL: http://djberg96.livejournal.com/data/rss
Feed Description: A blog on Ruby and other stuff.
Occasionally you want to check the type of an argument within your C extension. I do this to avoid potentially bizarre and fatal error messages, bad data and even core dumps. Things that won't make any sense to an end user.
Let's say we have a class Bar, with a singleton method "getnum" that looks like this:
/* Take a number, multiply it by 3, return it */
static VALUE getnum(VALUE rbVal){
int x = 0;
x = FIX2INT(rbVal) * 3;
return FIX2INT(x);
}
With this code Bar.getnum(10) will return 30. What will happen if I do Bar.getnum("a")? In my test, the result was 1592196. Not exactly what we were expecting.
There are a few ways you can check an object's type within a C extension. One way is to use the TYPE macro function, and check it explicitly.
if(TYPE(rbVal) != T_FIXNUM)
rb_raise(rb_eTypeError,"argument must be a fixnum");
Another option is to use one of the builtin type check boolean macros (or whatever you want to call them).
if(!FIXNUM_P(rbVal))
rb_raise(rb_eTypeError,"argument must be a fixnum");
But that's still too much typing if all you want is an automatic failure if it's not a Fixnum. The best and easiest solution is to use one of the "NUM" functions.
x = NUM2INT(rbVal) * 3;
Functions like INT2NUM, LONG2NUM, NUM2INT, etc, automatically include a type check and raise a TypeError if they fail. While this example actually converts and assigns a value to 'x', there are times when I use it in void context strictly as a type checking mechanism.
NUM2INT(rbSomeVal); /* Make sure it's a Fixnum */
Note that the builtin type check is a wee bit slower than it's non type checking cousin. I generally don't type check when I'm converting C data types to Ruby objects in cases where I absolutely know that the return value is an integer.