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;
}