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.

···

--
Iñaki Baz Castillo
<ibc@aliax.net>

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

···

Am 08.05.2012 00:21, schrieb Iñaki Baz Castillo:

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.

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

···

--
Posted via http://www.ruby-forum.com/.

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).

···

Iñaki Baz Castillo <ibc@aliax.net> 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?

Hi, thanks to both. Comments inline:

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

3) 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)

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

  rb_define_private_method(mMyModule, "_init_c_data", mMyModule_init_c_data, 2);

5) 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/8 Hans Mackowiak <lists@ruby-forum.com>:

--
Iñaki Baz Castillo
<ibc@aliax.net>

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:

···

2012/5/9 Eric Wong <normalperson@yhbt.net>:

Ruby itself uses this idiom in some places:

   DATA\_PTR\(self\) = ptr;

---------------

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.

--
Iñaki Baz Castillo
<ibc@aliax.net>

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?

···

2012/5/10 Iñaki Baz Castillo <ibc@aliax.net>:

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.

--
Iñaki Baz Castillo
<ibc@aliax.net>

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 Castillo <ibc@aliax.net>:

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?

--
Iñaki Baz Castillo
<ibc@aliax.net>