Я работаю с тем, что должно быть довольно простой итерацией.Я понимаю, что мог бы сделать это с помощью кода 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, кажется, не имеют много примеров rb_block_call с argc / argv ... Не более одного или двух, иЯ считаю, что все они просто передают значения внутренне, а не используют их.
Мысли?