Inheritance from C object with different parameter count

Thank you, this was exact the information I was looking for.
Greetings,
Geert.

···

-----Original Message-----
From: George Ogata [mailto:g_ogata@optushome.com.au]
Sent: Wednesday, February 01, 2006 10:47 PM
To: ruby-talk ML
Subject: Re: inheritance from C object with different parameter count

"Geert Fannes" <Geert.Fannes@ikanconsulting.com> 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: