From: Joel VanderWerf Date: 2002-09-25T04:21:07+09:00 Subject: Re: Object-Oriented struct Model in C William Djaja Tjokroaminata wrote: > Hi, > > In the Ruby-C world, we can make an association between a Ruby class and a > C struct through the use of Data_Wrap_Struct(). But in the Ruby world, a > Ruby class can inherit from a parent, which in turn inherits from its > parent, and so on. Suppose each class in this inheritance chain has > different data in its struct. Is the following a good model for the > corresponding C struct's? > > typedef struct > { > /* pointer to parent */ > void *parentData; > void (*parentMarkFunc) (void*); > void (*parentFreeFunc) (void*); > > /* class-specific data */ > .... > } sClass_Any; > > (Is there any reference on how to do object-orientation in C? Well, C++ > is excluded for now.) My turn to sound like a broken record... The CShadow module (in cgenerator on RAA) gives you this functionality. It opts for time rather than space efficiency, so there is a lot of code duplication. However, it is managed for you, so this is only a problem if you want small executables. Here's a simple example with inheritance: require 'cgen/cshadow' class Parent include CShadow shadow_attr_accessor :ruby_str => String shadow_attr_accessor :c_int => "int c_int" end class Child < Parent shadow_attr_accessor :obj => Object end Parent.commit # we're done adding attrs and methods, so make. x = Child.new x.ruby_str = "foo" x.obj = [1,2,3] x.c_int = 3 The generated code looks, in part, as follows. Note that CGenerator/CShadow produces quite readable C code; I have snipped but not changed anything. In the Parent.h header file we have: typedef struct Parent_Shadow { VALUE self; VALUE ruby_str; // String; int c_int; } Parent_Shadow; typedef struct Child_Shadow { /* Parent_Shadow members */ VALUE self; VALUE ruby_str; // String; int c_int; VALUE obj; // Object; } Child_Shadow; Again, members are duplicated, which would be awful if you wanted to manually maintain the code, but who wants to do that :-) In the Parent.c file, we have: void mark_Parent_Shadow(Parent_Shadow *shadow) { rb_gc_mark((void *)shadow->self); rb_gc_mark((void *)shadow->ruby_str); } void mark_Child_Shadow(Child_Shadow *shadow) { rb_gc_mark((void *)shadow->self); rb_gc_mark((void *)shadow->ruby_str); rb_gc_mark((void *)shadow->obj); } and similarly for the free functions, persistence functions, etc. The #new method generated by CShadow is where rb_obj_call_init() gets called. Note that initialization of shadow attributes is customizable. VALUE new_module_Child_singleton_method(int argc, VALUE *argv, VALUE self) { VALUE object; Child_Shadow *shadow; object = Data_Make_Struct(self, Child_Shadow, mark_Child_Shadow, free_Child_Shadow, shadow); shadow->self = object; shadow->ruby_str = Qnil; shadow->obj = Qnil; rb_obj_call_init(object, argc, argv); return object; } The #ruby_str= method only needs to be defined for Parent, because, of course, Ruby itself handles inheritance of methods. Note the type checking, which is done for the ruby_str member but not for the obj member. VALUE ruby__str_equals_module_Parent_method(int argc, VALUE *argv, VALUE self) { VALUE arg; Parent_Shadow *shadow; rb_scan_args(argc, argv, "1", &arg); if (!NIL_P(arg) && rb_obj_is_kind_of(arg, module_String) != Qtrue) rb_raise(module_TypeError, "argument arg declared String but passed %s.", STR2CSTR(rb_funcall( rb_funcall(arg, ID_type, 0), ID_to__s, 0))); Data_Get_Struct(self, Parent_Shadow, shadow); shadow->ruby_str = arg; return arg; }