Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

How can I bind a C/C++ structure to Ruby?

I need some advice how can I bind a C/C++ structure to Ruby. I've read some manuals and I found out how to bind class methods to a class, but I still don't understand how to bind structure fields and make them accessible in Ruby.

Here is the code I'm using:

myclass = rb_define_class("Myclass", 0);
...
typedef struct nya
{
    char const* name;
    int age;
} Nya;
Nya* p;
VALUE vnya;
p = (Nya*)(ALLOC(Nya));
p->name = "Masha";
p->age = 24;
vnya = Data_Wrap_Struct(myclass, 0, free, p);
rb_eval_string("def foo( a ) p a end"); // This function should print structure object
rb_funcall(0, rb_intern("foo"), 1, vnya); //  Here I call the function and pass the object into it

The Ruby function seems to assume that a is a pointer. It prints the numeric value of the pointer instead of it's real content (i.e., ["Masha", 24]). Obviously the Ruby function can't recognize this object —I didn't set the object's property names and types.

How can I do this? Unfortunately I can't figure it out.

like image 735
l1nx Avatar asked Jan 29 '26 14:01

l1nx


1 Answers

You have already wrapped your pointer in a Ruby object. Now all you have to do is define how it can be accessed from the Ruby world:

/* Feel free to convert this function to a macro */
static Nya * get_nya_from(VALUE value) {
    Nya * pointer = 0;
    Data_Get_Struct(value, Nya, pointer);
    return pointer;
}

VALUE nya_get_name(VALUE self) {
    return rb_str_new_cstr(get_nya_from(self)->name);
}

VALUE nya_set_name(VALUE self, VALUE name) {
    /* StringValueCStr returns a null-terminated string. I'm not sure if
       it will be freed when the name gets swept by the GC, so maybe you
       should create a copy of the string and store that instead. */
    get_nya_from(self)->name = StringValueCStr(name);
    return name;
}

VALUE nya_get_age(VALUE self) {
    return INT2FIX(get_nya_from(self)->age);
}

VALUE nya_set_age(VALUE self, VALUE age) {
    get_nya_from(self)->age = FIX2INT(age);
    return age;
}

void init_Myclass() {
    /* Associate these functions with Ruby methods. */
    rb_define_method(myclass, "name",  nya_get_name, 0);
    rb_define_method(myclass, "name=", nya_set_name, 1);
    rb_define_method(myclass, "age",   nya_get_age,  0);
    rb_define_method(myclass, "age=",  nya_set_age,  1);
}

Now that you can access the data your structure holds, you can simply define the high level methods in Ruby:

class Myclass
  def to_a
    [name, age]
  end

  alias to_ary to_a

  def to_s
    to_a.join ', '
  end

  def inspect
    to_a.inspect
  end
end

For reference: README.EXT

like image 61
Matheus Moreira Avatar answered Feb 01 '26 05:02

Matheus Moreira



Donate For Us

If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!