Reputation: 414
I have an array (can be more than just one array) in C. I need to build an interface so that ruby can modify/read the array values. I am building ruby modules in C so they later are used in ruby.
C file:
#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 file:
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
So all this works great, but now I need to be able to do the same with an array. What I tried:
C file:
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);
What I am trying to do is name the set/get method to give an impression in ruby that I am "using" an array when is just really an interface to interact with the array that exists in C. The C file compiles fine but when I try to call the method from Ruby, it complains saying that "array" is not a method
Ruby:
c = Thing.new
c.array[0] #=> NoMethodError (undefined method `array' for #<Thing:0x00000234523>)
What would be the best way to achieve this? (It must work for 2D arrays as well)
NOTE: Please edit my question if you find any information redundant.
Upvotes: 1
Views: 184
Reputation: 22325
What you want is not exactly possible. There is only one way that Ruby interprets this statement:
c.array[0]
That's equivalent to
c.array().[](0)
In other words, two method calls: array
with no arguments called on c
and then []
with one argument called on the return value of array
. If that's the syntax you want, then you'll need to define your classes in a way such that these methods exist: ParentModule
will need an array
method that returns something responding to []
. Since you don't want this to be an actual Array, you'll need to define another object with the []
method (this object can call back to ParentModule
to do whatever you want).
Upvotes: 1
Reputation: 79733
You can use []
and []=
as the method names to make your object appear array-like in Ruby:
rb_define_method(ParentModule, "[]", get_array, 1);
rb_define_method(ParentModule, "[]=", set_array, 2);
[]
takes a single argument, the index of the item you want to look at, and []=
takes two arguments, the index and the new value.
You can then implement them something like this:
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;
}
(Obviously this is just a simple example to show the idea, in reality you would want to check the index so it isn’t out of bounds, and also check the values so they make sense for the array type).
These methods will then be directly available on your object:
c = Thing.new
c[0] = 3
p c[0]
Upvotes: 0