How to use Data_Wrap_Struct to assign the DATA VALUE to an exsiting Ruby object?

Hi, my code receives an arbitrary klass name (provided by the user)
and I need to create an instance of such a klass and also wrap a C
pointer within it.

Unfortunately Data_Wrap_Struct(VALUE class, void (*mark)(), void
(*free)(), void *ptr") requires a class VALUE rather than a single
object/instance. So I expect that I cannot attach/wrap a C DATA
pointer into an existing Ruby object and, instead, I need to store
the DATA pointer within an internal attribute of the object, am I
right?

Thanks a lot.

Am 08.05.2012 00:21, schrieb Iñaki Baz C.:

Thanks a lot.

No, it’s possible, but not documented. I’m not entirely sure how I did
this, but it involved reading the bare RDATA struct from the the object
and changing its internal pointer directly (maybe you should try hunting
around in ruby.h/intern.h for the exact struct definitions). My usecase
however was a bit different; I had to place a pointer already at the
class instanciation, and then replace it later with a pointer to
something different, i.e. my object was already wrapping something. I’m
not sure if the trick via RDATA does work with objects that haven’t been
assigned something right from the beginning.

Vale,
Marvin

if you can use c++ in your binding you can use an std::map<VALUE,T> to
associate an ruby object with an other pointer

otherwise if you show us your code, we maybe find better ways

Iñaki Baz C. [email protected] wrote:

Hi, my code receives an arbitrary klass name (provided by the user)
and I need to create an instance of such a klass and also wrap a C
pointer within it.

Unfortunately Data_Wrap_Struct(VALUE class, void (*mark)(), void
(*free)(), void *ptr") requires a class VALUE rather than a single
object/instance. So I expect that I cannot attach/wrap a C DATA
pointer into an existing Ruby object and, instead, I need to store
the DATA pointer within an internal attribute of the object, am I
right?

Ruby itself uses this idiom in some places:

DATA_PTR(self) = ptr;

Seriously, the best way to learn C extensions is to read Ruby itself
(and its C extensions).

Hi, thanks to both. Comments inline:

2012/5/8 Hans M. [email protected]:

if you can use c++ in your binding you can use an std::map<VALUE,T> to
associate an ruby object with an other pointer

I use C and honestly I wouldn’t like to mix C++ stuff in my extension.

otherwise if you show us your code, we maybe find better ways

Ok, my code does the following:

  1. First in Ruby land the high-user calls a method by passing a class
    (klass). Such a class must include a specific module MyModule (my code
    checks it).

  2. Then still in Ruby land, the method allocates an instance of such a
    klass:

obj = klass.new

  1. And the the method calls to another method defined in my Ruby C
    extension, by passing some extra arguments:

obj._init_c_data(ip, port)

  1. Note that the _init_c_data() method is defined for MyModule:

rb_define_private_method(mMyModule, “_init_c_data”,
mMyModule_init_c_data, 2);

  1. Within mMyModule_init_c_data C function, I need to allocate a DATA
    struct within “obj” (so “self” argument). Currently I create an
    internal attribute “@cdata” for “obj” and store there the DATA struct:

cCData = rb_define_class(“CData”, rb_cObject);

struct my_cdata* cdata = ALLOC(struct my_cdata);
rb_ivar_set(self, rb_intern(“@cdata”), Data_Wrap_Struct(cCData,
NULL, NULL, cdata));

This works ok. However I wonder if it’s possible to wrap the cdata
struct within the “obj” object instead, but take into account that
“obj” was previously allocated, and also note that the class of “obj”
is provided by the high-user, so I cannot use rb_define_alloc_func()
since I don’t know the name of the klass.

Thanks a lot.

2012/5/9 Eric W. [email protected]:

Ruby itself uses this idiom in some places:

   DATA_PTR(self) = ptr;

Thanks Eric. I’ve taken a look to file.c in Ruby sources which uses
this stuf in functions rb_stat_init() and rb_stat_init_copy().
Unfortunatelly I get a segmentfault after using DATA_PTR(self)=ptr,
let me show what I do:

Ruby land:

sock = klass.allocate
sock.init_udp_socket bind_ip, bind_port

C land:

VALUE MyModule_init_udp_socket(VALUE self, VALUE rb_bind_ip, VALUE
rb_bind_port)
{
[…]
struct my_udp_cdata* cdata = ALLOC(struct my_udp_cdata);

cdata->xxxx = xxxx;
cdata->xxxx = xxxx;

DATA_PTR(self) = cdata;

[…]
return self;
}

This still does NOT crash, but if after the C function I call to any
attribute reader of sock or use sock.instance_variable_set(), then
it crashes.

Am I doing something wrong? Thanks a lot.

2012/5/10 Iñaki Baz C. [email protected]:

So I think that using DATA_PTR(self) is not what I expected. IMHO it
completely replaces the current C data of the Ruby object, while the
only I want is to “attach” a C structure to a given Ruby object. Am I
right?

If I use klass.new instead of klass.allocate, then it does not crash.
However I cannot use the Data in other methods of the klass:

struct my_udp_cdata *cdata = DATA_PTR(self);

this returns ugly data, it’s not the data I previously stored using:

DATA_PTR(self) = cdata;

Probably it makes no sense what I’m doing.

2012/5/10 Iñaki Baz C. [email protected]:

DATA_PTR(self) = cdata;

[…]
return self;
}

This still does NOT crash, but if after the C function I call to any
attribute reader of sock or use sock.instance_variable_set(), then
it crashes.

DATA_PTR is defined as follows:

#define DATA_PTR(dta) (RDATA(dta)->data)
#define RDATA(obj) (R_CAST(RData)(obj))
#define R_CAST(st) (struct st*)

struct RData {
struct RBasic basic;
void (dmark)(void);
void (dfree)(void);
void *data;
};

struct RBasic {
VALUE flags;
VALUE klass;
};

So I think that using DATA_PTR(self) is not what I expected. IMHO it
completely replaces the current C data of the Ruby object, while the
only I want is to “attach” a C structure to a given Ruby object. Am I
right?