Как я могу связать структуру C / C ++ с Ruby? - PullRequest
3 голосов
/ 24 февраля 2012

Мне нужен совет, как связать структуру C / C ++ с Ruby. Я прочитал несколько руководств и узнал, как связать методы класса с классом, но я все еще не понимаю, как связать поля структуры и сделать их доступными в Ruby.

Вот код, который я использую:

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

Кажется, функция Ruby предполагает, что a является указателем. Он печатает числовое значение указателя вместо его реального содержимого (т. Е. ["Masha", 24]). Очевидно, что функция Ruby не может распознать этот объект - я не задавал имена и типы свойств объекта.

Как я могу это сделать? К сожалению, я не могу понять это.

Ответы [ 3 ]

3 голосов
/ 24 февраля 2012

Вы уже завернули указатель в объект Ruby. Теперь все, что вам нужно сделать, это определить, как к нему можно получить доступ из мира Ruby:

/* 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);
}

Теперь, когда вы можете получить доступ к данным, которые содержит ваша структура, вы можете просто определить высокоуровневые методы в 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

Для справки: README.EXT

1 голос
/ 24 февраля 2012

Это не прямой ответ на ваш вопрос о структурах, но это общее решение проблемы переноса классов C ++ в Ruby.

Вы можете использовать SWIG для переноса классов, структур и функций C / C ++. В случае структуры это сжигает дом, чтобы жарить яйцо. Однако, если вам нужен инструмент для быстрого преобразования классов C ++ в Ruby (и 20 других языков), SWIG может быть вам полезен.

В вашем случае со структурой вам просто нужно создать файл .i, который включает (в простейшем случае) строку #include <your C++ library.h>.

P.S. Еще раз, это не прямой ответ на ваш вопрос, касающийся этой единственной структуры, но, возможно, вы могли бы использовать более общее решение, и в этом случае это может вам помочь.

0 голосов
/ 09 марта 2012

Другой вариант - использовать RubyInline - он имеет ограниченную поддержку для преобразования типов C и Ruby (таких как int, char * и float), а также поддерживает доступ к структурам C - см. accessor метод в API .

...