I'm working with what should be a fairly basic iteration. I understand that I could accomplish it with Ruby code, but I am working already in a C extension, so I would prefer to keep this function in C with the rest of the code- especially since this should work (one way or another) without issue.
The issue is with rb_block_call. Here is how README.EXT describes rb_block_call:
VALUE rb_block_call(VALUE recv, ID mid, int argc, VALUE * argv, VALUE (*func) (ANYARGS), VALUE data2)
Calls a method on the recv, with the method name specified by the symbol mid, supplying func as the block. func will receive the value from yield as the first argument, data2 as the second, and argc/argv as the third/fourth arguments.
So, my understanding (verified by looking at Ruby internals), is that the receiving function should look like:
VALUE function( VALUE rb_yield_value, VALUE data2, int argc, VALUE argv );
And here we hit our problem. In my use case (which I will include below), rb_yield_value and data2 are passed as expected; argc, on the other hand, is always set to 1, argv[ 0 ] is rb_yield_value, argv[ 1 ] is false, argv[ 2 ] is rb_yield_value, argv[ 3 ] throws an exception.
It does not matter what I pass for argc and argv; passing 0 and NULL results the same, as does 1 and a VALUE set to Qtrue. Everything with argc/argv remains as described.
Here is the code I am working with:
VALUE rb_RPBDB_DatabaseObject_internal_cursorForCallingContext( VALUE rb_self ) {
// when we are looking for the contextual iterator, we look up the current backtrace
// at each level of the backtrace we have an object and a method;
// if this object and method match keys present in self (tracking calling contexts for iteration in this iteration class) return cursor
VALUE rb_cursor_context_storage_hash = rb_RPBDB_DatabaseObject_internal_cursorContextStorageHash( rb_self );
VALUE rb_cursor = Qnil;
if ( RHASH_SIZE( rb_cursor_context_storage_hash ) ) {
rb_block_call( rb_mKernel,
rb_intern( "each_backtrace_frame" ),
1,
& rb_cursor_context_storage_hash,
rb_RPBDB_DatabaseObject_internal_each_backtrace_frame,
rb_cursor );
}
return rb_cursor;
}
// walk up the stack one frame at a time
// for each frame we need to see if object/method are defined in our context storage hash
VALUE rb_RPBDB_DatabaseObject_internal_each_backtrace_frame( VALUE rb_this_backtrace_frame_hash,
VALUE rb_cursor_return,
int argc,
VALUE* args ) {
// why are we getting 3 args when argc is 1 and none of the 3 match what was passed?
VALUE rb_cursor_context_storage_hash = args[ 0 ];
// each frame is identifiable as object/method
VALUE rb_this_frame_object = rb_hash_aref( rb_this_backtrace_frame_hash,
ID2SYM( rb_intern( "object" ) ) );
VALUE rb_this_frame_method = rb_hash_aref( rb_this_backtrace_frame_hash,
ID2SYM( rb_intern( "method" ) ) );
// we likely have "block in ..." for our method; we only want the "..."
rb_this_frame_method = ID2SYM( rb_to_id( rb_funcall( rb_obj_as_string( rb_this_frame_method ),
rb_intern( "gsub" ),
2,
rb_str_new2( "block in " ),
rb_str_new2( "" ) ) ) );
VALUE rb_cursor_object_context_hash = rb_RPBDB_DatabaseObject_internal_cursorObjectContextStorageHash( rb_cursor_context_storage_hash,
rb_this_frame_object);
if ( RHASH_SIZE( rb_cursor_object_context_hash ) ) {
rb_cursor_return = rb_hash_aref( rb_cursor_object_context_hash,
rb_this_frame_method );
}
return rb_cursor_return;
}
Ruby internals don't seem to have many examples of rb_block_call with argc/argv... At most one or two, and I believe they all simply relay the values internally rather than using them.
Thoughts?