Domanda

I've written a simple Stacks and Queues library in C as an extension for Ruby and came across needing to write the same two lines of code whenever I needed to retrieve an instance variable (in this case in stack.h):

VALUE stack;
stack = rb_iv_get(self, "@stack");

From what I found, this approach is quite slow compared to actual Ruby code (by as much as 0.1 second) and not at all DRY. Is there any way I can extract that out, to some kind of global variable or function? I tried putting these two lines outside any of the functions but I need access to self with rb_iv_get(self, "@iv_name") so that throws me an error.

È stato utile?

Soluzione

I do not think your speed problem is directly to do with calls to rb_iv_get. You should first consider whether you need to keep the data in a Ruby object at all. It may be far more effective to have the data in a C data structure, and convert it to or from Ruby data as needed by methods your extension adds to the class.

However, if you need to manage a Ruby object as an integral part of a C extension, then you can keep the value as part of a C struct and implement an accessor for the Ruby side to see it if necessary. Then you don't need to call rb_iv_get at all to access the VALUE in C.

First you will need to understand how to use Data_Wrap_Struct and Data_Get_Struct macros from ruby.h - the Extending Ruby chapter of Programming Ruby is a good place to see this in action, with the CD Jukebox example.

Then, armed with this, you can make one of your structure components a VALUE:

typedef struct my_structure {
  int some_number;
  VALUE some_ruby_val;
} MyStructure;

In addition to the CD Jukebox example, you will need to implement a function that marks some_ruby_val as being in use (if you don't do this, Ruby's garbage collector will recycle the memory, and you could end up with some other object being pointed to - usually that will get you a segfault):

void my_structure_gc_mark( MyStructure *mine ) {
  rb_gc_mark( mine->some_ruby_val );
  return;
}

When you use Data_Wrap_Struct, you tell it about this function, as well as the destroy function documented in the link:

Data_Wrap_Struct( klass, my_structure_gc_mark, my_structure_destroy, mine );

Ruby then takes care of calling it when necessary.

Finally, to get your extension-held Ruby data, you just unwrap the structure and refer it like any other part of a struct. By way of example, here is how you would implement the accessor method for the above:

VALUE my_ext_get_some_ruby_val( VALUE self ) {
  MyStructure *mine;
  Data_Get_Struct( self, MyStructure, mine );
  return mine->some_ruby_val;
}
Autorizzato sotto: CC-BY-SA insieme a attribuzione
Non affiliato a StackOverflow
scroll top