Inheritance from C object with different parameter count


#1

Hello,

I’m trying to create a derived class (ruby class) from a C-implemented
class. Everything works fine, until I tried to add a parameter to the
derived class. When I derive from a pure ruby base class, everything
works as should be, extra parameter or not. Does someone know what is
going on or what I am doing wrong?

I am getting the following output:

gfannes@lt-gfannes help $ ruby test.rb

test.rb:21:in `new’: wrong number of arguments (1 for 0) (ArgumentError)

    from test.rb:21

This is the ruby file containing the derivation from the base class. If
bCLib is set to false, things work as should be.

##############test.rb######################

bCLib=true

if bCLib

require(‘base.so’)

else

class Base

def initialize()

  puts("base")

end

end

end

class Derived < Base

def initialize(parameter)

puts("init derived")

super()

puts("finished super")

end

end

Derived.new(‘parameter’)

##########################################

This is the C-file that implements the base class in C
######################base.c#####################

#include “ruby.h”

#include <stdio.h>

#include <malloc.h>

typedef struct{

double dValue;

} baseStruct;

VALUE f_new0(VALUE klass)

{

printf(“f_new0.\n”);

VALUE rBase;

baseStruct *pBase=malloc(sizeof(baseStruct));

rBase = Data_Wrap_Struct(klass, 0, free, pBase);

printf(“before rb_obj_call_init.\n”);

rb_obj_call_init(rBase, 0, 0);

printf(“f_new0 finished.\n”);

return rBase;

}

VALUE f_initialize0(VALUE self)

{

printf(“f_initialize0.\n”);

return self;

}

VALUE cBase;

void Init_base()

{

cBase=rb_define_class(“Base”,rb_cObject);

rb_define_singleton_method(cBase, “new”, f_new0, 0);

rb_define_method (cBase, “initialize”, f_initialize0, 0); }
#################################################

Everything is compiled using extconf.rb

#############extconf.rb##########################

require ‘mkmf’

create_makefile(‘base’)

#################################################


#2

On Mon, Jan 30, 2006 at 06:12:13PM +0900, Geert F. wrote:

Hello,

I’m trying to create a derived class (ruby class) from a C-implemented
class. Everything works fine, until I tried to add a parameter to the
derived class. When I derive from a pure ruby base class, everything
works as should be, extra parameter or not. Does someone know what is
going on or what I am doing wrong?

Hmmm... weird. If you comment the "super()" call in 

Derived#initialize, it
dies with the exact same error. In fact, you don’t even get the “init
derived”
message.

After investigating a little, I found out that changing the 

definition of
new to:

rb_define_singleton_method(cBase, "new", f_new0, -1);

makes it work. I don’t know if that’s entirely correct (I’m not very
familiar
with C extensions), but it works :slight_smile: I guess the problem is that, as you
don’t
redefine the “new” method in the class Derived, you get the Base one, so
it
chokes. I don’t know if the usual thing here is always defining “new”
methods
with any parameter count…

HTH,

#3

“Geert F.” removed_email_address@domain.invalid writes:

I’m trying to create a derived class (ruby class) from a C-implemented
class. Everything works fine, until I tried to add a parameter to the
derived class. When I derive from a pure ruby base class, everything
works as should be, extra parameter or not. Does someone know what is
going on or what I am doing wrong?

Your C code does not do:

class Base
def initialize()
puts(“base”)
end
end

It’s more like:

class Base
def self.new

end
def self.initialize

end
end

Thus, Derived.new(arg) is an error, since it calls Base.new, which
you’ve defined to take no args.

Note that Blah.new is a completely different method to
Blah#initialize. Blah.new would be a singleton method of the Class
instance Blah, but you usually don’t want that, as the standard
Class#new is sufficiently magical to do everything you need. In
pseudoruby, it looks like:

class Class
def new(*args)
object = self.alloc_func
object.initialize(*args)
return object
end
end

Object#dup and Object#clone are similar:

class Object
def dup(other)
object = self.alloc_func
object.initialize_copy(other)
return object
end
def clone(other)
object = self.alloc_func
object.singleton_class = other.singleton_class.copy
# … some other diddly bits …
object.initialize_copy(other)
return object
end
end

So what you normally do is:

– define the alloc func to alloc a ruby object from heap memory:
VALUE Thingy_allocate(VALUE klass) {
struct Thingy *thingy;
return Data_Make_Struct(klass, struct Thingy,
Thingy_mark, Thingy_free, thingy);
}

– define an #initialize method:
VALUE Thingy_initialize(VALUE self, …args…) {

}

– define an #initialize_copy method:
VALUE Thingy_initialize_copy(VALUE self, VALUE other) {

}

– bind 'em:
void Init_libthingies {
VALUE cThingy;
cThingy = rb_define_class(“Thingy”, rb_cObject);
rb_define_alloc_func(cThingy, Thingy_allocate);
rb_define_method(cThingy, “initialize”, Thingy_initialize,
num_args);
rb_define_method(cThingy, “initialize”, Thingy_initialize_copy,
1);

}

Of course, #initialize_copy is optional (in fact, so is #initialize),
but the point is that doing things this way means you only have to do
the malloc-ing once (in the alloc func), and everywhere else you’re
dealing with a perfectly valid ruby object. No need to call
#initialize or obj_call_init() or worry about your object not being
allocated if #initialize is overriden or gets interrupted by an
exception… just let the standard methods call your hooks and lie
back. :slight_smile: