Как создать интерфейс Ruby для массива C - PullRequest
0 голосов
/ 08 февраля 2019

У меня есть массив (может быть больше, чем один массив) в C. Мне нужно создать интерфейс, чтобы ruby ​​мог изменять / читать значения массива.Я собираю ruby-модули в C, чтобы они потом использовались в ruby.

C-файл:

#include <ruby.h>
VALUE ParentModule;
uint8 variable = 7;
uint8 array[2];

VALUE get_variable(VALUE self)
{
  return INT2NUM(variable);
}
VALUE set_variable(VALUE self, VALUE x)
{
  variable = NUM2UINT(x);
  return Qnil;
}

void Init_extension(void)
{
  ParentModule = rb_define_module("ParentModule");
  rb_define_method(ParentModule, "variable", get_variable, 0);
  rb_define_method(ParentModule, "variable=", set_variable, 1);
}

Ruby-файл:

class Thing
  def initialize
    extend ParentModule
  end
end
c = Thing.new
c.variable #=> will return the value 7
c.variable= 10 #=> will write 10 to variable in the C section.
c.variable #=> returns 10

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

C файл:

VALUE get_array_0(VALUE self)
{
  return INT2NUM(array[0]);
}
VALUE set_array_0(VALUE self, VALUE x)
{
  array[0] = NUM2UINT(x);
  return Qnil;
}

/* this line is inside Init_extension function */
rb_define_method(ParentModule, "array[0]", get_array_0, 0);

То, что я пытаюсь сделать, это назвать метод set / get, чтобы создать впечатление в ruby, что я "использую" массив, когдаэто просто интерфейс для взаимодействия с массивом, который существует в C. Файл C компилируется нормально, но когда я пытаюсь вызвать метод из Ruby, он жалуется, говоря, что «массив» не является методом

Ruby:

c = Thing.new
c.array[0] #=> NoMethodError (undefined method `array' for #<Thing:0x00000234523>)

Каков наилучший способ добиться этого?(Это должно работать и для двумерных массивов)

ПРИМЕЧАНИЕ. Пожалуйста, отредактируйте мой вопрос, если найдете какую-либо информацию избыточной.

Ответы [ 2 ]

0 голосов
/ 09 февраля 2019

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

c.array[0]

Это эквивалентно

c.array().[](0)

Другими словами, два вызова метода: array без аргументов, вызываемых c а затем [] с одним аргументом, вызываемым на возвращаемое значение array.Если это тот синтаксис, который вы хотите, то вам нужно определить свои классы таким образом, чтобы существовали эти методы: ParentModule потребуется метод array, который возвращает что-то, отвечающее [].Поскольку вы не хотите, чтобы это был фактический массив, вам нужно определить другой объект с помощью метода [] (этот объект может вызывать ParentModule, чтобы делать все, что вы захотите).

0 голосов
/ 08 февраля 2019

Вы можете использовать [] и []= в качестве имен методов, чтобы сделать ваш объект похожим на массив в Ruby:

rb_define_method(ParentModule, "[]", get_array, 1);
rb_define_method(ParentModule, "[]=", set_array, 2);

[] принимает один аргумент, индекс элементавы хотите посмотреть, и []= принимает два аргумента, индекс и новое значение.

Затем вы можете реализовать их примерно так:

VALUE get_array(VALUE self, VALUE idx)
{
  return INT2NUM(array[NUM2INT(idx)]);
}

VALUE set_array(VALUE self, VALUE idx, VALUE x)
{
  array[NUM2INT(idx)] = NUM2UINT(x);
  return Qnil;
}

(Очевидно, это простопростой пример, чтобы показать идею, в действительности вы хотели бы проверить индекс, чтобы он не выходил за пределы, а также проверить значения, чтобы они имели смысл для типа массива).

Эти методытогда будьте непосредственно доступны на вашем объекте:

c = Thing.new
c[0] = 3
p c[0]
Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...