On Using Data_Wrap_Struct

Rubyists,

In my extension, I have a C struct like

struct _bar {
int n;
char *msg;
} Bar;

I have a defined class Foo in the extension.
Foo has an array member called @arr.

I want to be able to push values of Bar onto
@arr and to get at them from my ruby code,
something like:

f = Foo.new
f.run # --> runs and adds Bars to @arr
f.arr.each { |bar| puts “#{bar.n} --> #{bar.msg}” }

I think that I need to use Data_Wrap_Struct, however
I am not sure.

Guidance, anyone?

Thanks, once again.

-mark.

If Bar is just some data structure, you’ll need to convert both n
and the string pointed to by msg to Ruby objects and then store
them in a Struct object that corresponds to your Bar structure. Then push
the Struct object onto the array and store the VALUE that represents the
array in the Foo structure. Don’t forget to mark the array VALUE within
Foo’s mark function.

You can create a Struct class that corresponds to your Bar
structure using rb_struct_define, like this:

VALUE Class_Bar;
Class_Bar = rb_struct_define(NULL, “n”, “msg”, 0);

This is the C equivalent of the Ruby statement

Bar = Struct.new(nil, "n", "msg");

To make a Ruby Bar object equivalent to an instance of the C
Bar structure (assuming msg is a null-delimited string):

Bar c_bar = {123, “a msg”};
VALUE ruby_bar;

ruby_bar = rb_funcall(Class_Bar, rb_intern(“new”), 2,
INT2FIX(c_bar.n), rb_str_new2(c_bar.msg));

Use rb_ary_push to push ruby_bar onto the end of your array.

Check out Chap 17 in the Pickaxe.

···

On Thu, 24 Oct 2002 05:23:27 +0900, Mark Probert wrote:

Rubyists,

In my extension, I have a C struct like

struct _bar {
int n;
char *msg;
} Bar;

I have a defined class Foo in the extension.
Foo has an array member called @arr.

I want to be able to push values of Bar onto
@arr and to get at them from my ruby code,
something like:

f = Foo.new
f.run # → runs and adds Bars to @arr
f.arr.each { |bar| puts “#{bar.n} → #{bar.msg}” }

I think that I need to use Data_Wrap_Struct, however
I am not sure.

Guidance, anyone?

Thanks, once again.

-mark.

Tim,

Thank you so much for the info, it has done the trick.

One question on your post…

…Don’t forget to mark the array VALUE within
Foo’s mark function.

I would need to do this if I dynamically allocate
storage for msg, yes? If I statically allocate it

struct _bar {
int n;
char msg[64];
} Bar;

then I assume that the standard mechanisms will clean
up the mess I make?

Thanks, again.

-mark.

···

“Tim Hunter” cyclists@nc.rr.com wrote:

Hi Mark,

It depends on what you actually want to do. With the given example code
structure, the coupling is not bi-directional, i.e., when you have
something like this in Ruby

 bar.msg = 'a new string'

then the corresponding C’s cbar.msg is not automatically modified. If you
want automatic coupling from Ruby to C, then you need Data_Wrap_Struct.

Regards,

Bill

···

Tim Hunter cyclists@nc.rr.com wrote:

ruby_bar = rb_funcall(Class_Bar, rb_intern(“new”), 2,
INT2FIX(c_bar.n), rb_str_new2(c_bar.msg));

On Thu, 24 Oct 2002 05:23:27 +0900, Mark Probert wrote:

f = Foo.new
f.run # → runs and adds Bars to @arr
f.arr.each { |bar| puts “#{bar.n} → #{bar.msg}” }

I think that I need to use Data_Wrap_Struct, however
I am not sure.

No. Remember that Ruby will allocate storage from its heap for the
array that you’re using and for the copies of n and msg that you make
when you do INT2FIX(n) and rb_str_new2(msg) (or rb_str_new(msg, 64)).

As long as you need the array, (which I assume is as long as the Foo
object is alive) you must mark its VALUE whenever gc calls Foo’s mark
function. Otherwise gc will delete it out from under you. The array,
in turn, will take care of marking the VALUEs it contains, including
the n and msg VALUEs you push on it.

When you create a Foo object with Data_Wrap_Struct or
Data_Make_Struct, include a pointer to a “mark function”. GC will call
the mark function periodically, during the “mark” part of its mark &
sweep. It’s the mark function’s responsibility to mark any VALUEs
contained in the Foo object so that gc can tell that they’re still
alive. Just add a call to rb_gc_mark(my_ary) in the mark function.

This is from the Pickaxe, chapter 17:

"In order to participate in Ruby’s mark-and-sweep garbage collection
process, you need to define a routine to free your structure, and
possibly a routine to mark any references from your structure to other
structures. Both routines take a void pointer, a reference to your
structure. The mark routine will be called by the garbage collector
during its ``mark’’ phase. If your structure references other Ruby
objects, then your mark function needs to identify these objects using
rb_gc_mark(value). If the structure doesn’t reference other Ruby
objects, you can simply pass 0 as a function pointer. "

Good luck!

···

On Thu, 24 Oct 2002 00:53:16 GMT, Mark Probert probertm@acm.org wrote:

Tim,

Thank you so much for the info, it has done the trick.

One question on your post…

“Tim Hunter” cyclists@nc.rr.com wrote:

…Don’t forget to mark the array VALUE within
Foo’s mark function.

I would need to do this if I dynamically allocate
storage for msg, yes? If I statically allocate it

struct _bar {
int n;
char msg[64];
} Bar;

then I assume that the standard mechanisms will clean
up the mess I make?

Thanks, again.

-mark.

Mmmm… actually if the array is an instance variable, as Mark wrote

...Foo has an array member called @arr...

then we don’t need to explicitly mark it, as Ruby already does it for us.

Regards,

Bill

···

Tim Hunter Tim.Hunter@sas.com wrote:

As long as you need the array, (which I assume is as long as the Foo
object is alive) you must mark its VALUE whenever gc calls Foo’s mark
function. Otherwise gc will delete it out from under you. The array,
in turn, will take care of marking the VALUEs it contains, including
the n and msg VALUEs you push on it.

When you create a Foo object with Data_Wrap_Struct or
Data_Make_Struct, include a pointer to a “mark function”. GC will call
the mark function periodically, during the “mark” part of its mark &
sweep. It’s the mark function’s responsibility to mark any VALUEs
contained in the Foo object so that gc can tell that they’re still
alive. Just add a call to rb_gc_mark(my_ary) in the mark function.

Bill and Tim,

Thank you for the answers.

···

At 11:43 PM 10/24/2002 +0900, Bill wrote:

Mmmm… actually if the array is an instance variable, as Mark wrote

...Foo has an array member called @arr...

then we don’t need to explicitly mark it, as Ruby already does it for us.

It appears to be cleaning up okay. Then, I am using NT without
Purify or equiv…

-mark


Mark Probert
GNPS - Optera Metro 3000
Tel (613) 768-1082 (ESN: 398-1082)
Mob (613) 715-2045
email: probertm@nortelnetworks.com

Hi Mark,

Oh yes, there will be no harm in marking it twice. As long as the cost of
marking it multiple times is small, it should not create any problem.

Regards,

Bill

···

Mark Probert probertm@nortelnetworks.com wrote:

It appears to be cleaning up okay. Then, I am using NT without
Purify or equiv…