3

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.

Mike G
  • 746
  • 5
  • 19
l1nx
  • 31
  • 2

3 Answers3

3

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

Matheus Moreira
  • 17,106
  • 3
  • 68
  • 107
1

This is not a direct answer to your question about structures, but it is a general solution to the problem of porting C++ classes to Ruby.

You could use SWIG to wrap C/C++ classes, structs and functions. In the case of a structure, it's burning a house to fry an egg. However, if you need a tool to rapidly convert C++ classes to Ruby (and 20 other languages), SWIG might be useful to you.

In your case involving a structure, you just need to create a .i file which includes (in the simplest case) the line #include <your C++ library.h>.

P.S. Once more, it's not a direct answer to your question involving this one struct, but maybe you could make use of a more general solution, in which case this may help you.

Mike G
  • 746
  • 5
  • 19
MajesticRa
  • 13,770
  • 12
  • 63
  • 77
0

Another option is to use RubyInline - it has limited support for converting C and Ruby types (such as int, char * and float) and it also has support for accessing C structurs - see accessor method in the API.

Aleksander Pohl
  • 1,675
  • 10
  • 14