我正在处理应该是一个相当基本的迭代。我知道我可以使用 Ruby 代码来完成它,但我已经在使用 C 扩展,所以我更愿意将此函数与其余代码一起保存在 C 中 - 特别是因为这应该可以(以一种或另一种方式)在没有问题。
问题在于 rb_block_call。以下是 README.EXT 描述 rb_block_call 的方式:
VALUE rb_block_call(VALUE recv, ID mid, int argc, VALUE * argv, VALUE (*func) (ANYARGS), VALUE data2)
调用 recv 上的方法,方法名称由符号 mid 指定,提供 func 作为块。func 将从 yield 作为第一个参数接收值,data2 作为第二个参数,argc/argv 作为第三/第四个参数。
所以,我的理解(通过查看 Ruby 内部验证)是接收函数应该如下所示:
VALUE function( VALUE rb_yield_value, VALUE data2, int argc, VALUE argv );
在这里,我们遇到了问题。在我的用例(我将在下面包括)中, rb_yield_value 和 data2 按预期传递;另一方面,argc 始终设置为 1,argv[0] 为 rb_yield_value,argv[1] 为 false,argv[2] 为 rb_yield_value,argv[3] 抛出异常。
我为 argc 和 argv 传递了什么并不重要;传递 0 和 NULL 结果相同,1 和 VALUE 设置为 Qtrue 也是如此。带有 argc/argv 的所有内容都保持原样。
这是我正在使用的代码:
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 内部似乎没有很多带有 argc/argv 的 rb_block_call 示例......最多只有一两个,我相信它们都只是在内部传递值而不是使用它们。
想法?