I have see strange behaviour with class costructors in Ruby C extension.
See an example: we have a class Foo
that is a C extension and a class Bar
that inherits from Foo
:
extconf.rb
# extconf.rb
require 'mkmf'
create_makefile('foo/foo')
foo.c
// foo.c
#include "ruby.h"
#include <stdio.h>
VALUE
foo_new (VALUE class)
{
printf ("foo_new\n");
int *ptr;
VALUE tdata = Data_Wrap_Struct (class, 0, 0, ptr);
rb_obj_call_init (tdata, 0, 0);
return tdata;
}
VALUE
foo_init (VALUE self)
{
printf ("foo_init\n");
return self;
}
VALUE
foo_plus_one (VALUE self, VALUE x)
{
printf ("foo_plus_one\n");
return INT2FIX (FIX2INT (x) + 1);
}
void
Init_foo ()
{
VALUE foo = rb_define_class ("Foo", rb_cObject);
rb_define_singleton_method (foo, "new", foo_new, 0);
rb_define_method (foo, "initialize", foo_init, 0);
rb_define_method (foo, "plus_one", foo_plus_one, 1);
}
bar.rb
# bar.rb
require './foo'
class Bar < Foo
end
Ok let's see strange stuffs...
In this situation all go ok:
x = Bar.new
we get the 2 prints: foo_new
and foo_init
.
Ok good, BUT if we change the class Bar
in this way:
# bar.rb
require './foo'
class Bar < Foo
def initialize(param = 1)
end
end
we have the first strange stuff if we run
x = Bar.new
we get only 1 print: foo_new
. And foo_init
??
Ok, we can bypass this problem adding an explicit call to the constructor of Foo
:
# bar.rb
require './foo'
class Bar < Foo
def initialize(param = 1)
super()
end
end
We get the 2 prints: foo_new
and foo_init
if we call x = Bar.new
.
The second strange stuff is this: if we call
x = Bar.new(2)
we get the error
in `new': wrong number of arguments(1 for 0) (ArgumentError)
But the constructor of Bar
accept one parameter with default value.
Why this? Is this a Ruby bug?
(tested with ruby1.9.3-p0 [ x86_64 ])
You defined ::new
to take no arguments, so wrong number of arguments(1 for 0)
is expected. Anyway, ::new
shouldn't be redefined. The right way to do it is defining the ::allocate
method (::new
calls it internally).
This should work:
// foo.c
#include "ruby.h"
#include <stdio.h>
void foo_free (void *ptr) {
free (ptr);
}
VALUE foo_alloc (VALUE class) {
printf ("foo_alloc\n");
int *ptr = malloc(sizeof(int));
return Data_Wrap_Struct (class, 0, foo_free, ptr);
}
VALUE foo_init (VALUE self) {
printf ("foo_init\n");
return self;
}
void Init_foo (void) {
VALUE cFoo = rb_define_class ("Foo", rb_cObject);
rb_define_alloc_func (cFoo, foo_alloc);
rb_define_method (cFoo, "initialize", foo_init, 0);
}