The C functions you are looking for are rb_ivar_set
and rb_ivar_get
.
rb_ivar_set
takes three arguments:
- the
VALUE
-type Ruby receiver object - the
ID
-type ivar name to assign - the
VALUE
-type Ruby right-hand-side object.
rb_ivar_get
takes two arguments:
- the
VALUE
-type Ruby receiver object - the
ID
-type ivar name to retrieve
Ruby can store instance variables internally with many kinds of ID
-type name. The only variable names made visible from the Ruby layer, though, are the ones that begin with @
, e.g. rb_intern("@foo")
. Leaving out the @
makes the instance variable inaccessible from the Ruby layer, but allows you to store and access it from the C layer.
Here is your code sample with this technique implemented.
#include "ruby.h"
static ID id_push;
static VALUE t_init(VALUE self) {
VALUE arr;
arr = rb_ary_new();
rb_ivar_set(self, rb_intern("arr"), arr); /* <-- */
return self;
}
static VALUE t_add(VALUE self, VALUE obj) {
VALUE arr;
arr = rb_ivar_get(self, rb_intern("arr")); /* <-- */
rb_funcall(arr, id_push, 1, obj);
return arr;
}
VALUE cTest;
void Init_my_test() {
cTest = rb_define_class("MyTest", rb_cObject);
rb_define_method(cTest, "initialize", t_init, 0);
rb_define_method(cTest, "add", t_add, 1); id_push = rb_intern("push");
}
Test it out! It should run like this (I didn't compile the above, there may be typos):
require 'my_test'
class MyTest
def check
return @arr
end
end
t = MyTest.new
t.add(1) #=> [1]
t.check #=> nil
The #check
method goes looking for the @arr
instance variable and comes up empty-handed. Your instance variables are safe and sound, locked up in the C layer!