As usual with Ruby, there must be several ways to manage data across
the Ruby-C divide. Personally, when a class has to exists in the C
world, I define a structure with the name of the class. For example, for
a class called Framereader:
typedef struct
{
...
...
} framereader_stc;
This, of course, can include nested classes. Why not? Then, in Init I have:
cls_framereader=rb_define_class("Framereader",rb_cObject);
rb_define_singleton_method(cls_framereader,"new",new_framereader,2);
and the first two lines of the "new" C implementations are as follows:
VALUE new_framereader(VALUE self,VALUE v_x,VALUE v_y)
{
framereader_stc *s;
VALUE sdata=Data_Make_Struct(cls_framereader,framereader_stc,NULL,free_framereader,s);
...
...
The return value of "new" is:
...
...
return sdata;
}
This allocated data I make sure to free inside the free method
(free_framereader in the example):
static void free_framereader(void *p)
{
framereader_stc *s=(framereader_stc *)p;
...
...
free(s);
}
If I make sure that I free here what may have been allocated through
the lifetime of the object instance, there are no leaks. Often, my C
structures include pointers that are set to NULL at initialization,
and may be allocated at some point during runtime. In free, I have to
check if this has happened, and eventually free whatever memory
involved, As well as stop threads, close I/O units, and so on.
Then, in all C-defined methods, I get a pointer to my structure. For
example, if I had declared a method called 'unit' like this:
...
...
rb_define_method(cls_framereader,"unit",framereader_unit,0);
...
...
the C code for the method will begin as follows:
VALUE framereader_unit(VALUE self)
{
framereader_stc *s;
Data_Get_Struct(self,framereader_stc,s);
...
...
This way, I am sure that the memory I use for the C side is properly
managed by Ruby's garbage collector. All the C memory space I use
either belongs to a class instance (and so I take care to free it in
the appropriate free method code), or I make sure to allocate and free
it within the same ruby-called method: generally, I write the 'malloc'
and 'free' lines one after the other, and then I insert whatever code
uses that memory in the middle.
When I need to pass something from C code that will be read from some
other C code, and that data will need to do part of the trip in
Ruby-land, I transform it into a Ruby String with rb_str_new. That
string is then part of the Ruby world, and thus managed by the Ruby
GC. But this does not happen too often. From experience I can say
that, if I come across problems of memory chunks being used in various
unrelated C code sections, if I find out that I'd need to juggle
around too many of these opaque data strings, the code is in need of a
bit of refactoring.
When I write a C part, that needs to be motivated (either for
performance reasons or because I need to interface external
libraries). If two C parts need to exchange data, either they have to
do it via Ruby (often, with translation from C structures to variables
that have a meaning for Ruby - that may have a cost in CPU cycles, but
I may find I can afford that), or I may find it possible and
beneficial to merge the two C parts into one - not just glue the two
codebases together, but merge the concepts represented by the classes
that the two C parts belong to.
This, of course, varies a lot from case to case.
HTH
Carlo
···
--
* Se la Strada e la sua Virtu' non fossero state messe da parte,
* K * Carlo E. Prelz - fluido@fluido.as che bisogno ci sarebbe
* di parlare tanto di amore e di rettitudine? (Chuang-Tzu)