Ruby 1.9.1-p378 C Extensão rb_block_call Estranheza
-
13-09-2020 - |
Pergunta
Eu estou trabalhando com o que deve ser bastante básico iteração.Eu entendo que eu pudesse realizar com o código de Ruby, mas estou trabalhando já em um C de extensão, então eu prefiro continuar esta função em C com o resto do código - especialmente desde que este deve trabalho (de uma forma ou de outra) sem problema.
O problema é com rb_block_call.Aqui está como LEIAME.EXT descreve rb_block_call:
VALUE rb_block_call(VALUE recv, ID mid, int argc, VALUE * argv, VALUE (*func) (ANYARGS), VALUE data2)
Chama um método em que o receber, com a nome do método especificado pelo símbolo mid, fornecimento de func como o bloco.func vai receber o valor do rendimento como o primeiro argumento, dados2, como o segundo, e argc/argv como o terceira/quarta argumentos.
Assim, o meu entendimento (não verificado olhando para Ruby internos), é que a função de recepção deve se parecer:
VALUE function( VALUE rb_yield_value, VALUE data2, int argc, VALUE argv );
E aqui vamos bater o nosso problema.No meu caso (que eu vou incluir abaixo), rb_yield_value e dados2 são passados como esperado;argc, por outro lado, é sempre definido para 1, argv[ 0 ] é rb_yield_value, argv[ 1 ] é falsa, argv[ 2 ] é rb_yield_value, argv[ 3 ] lança uma exceção.
Não importa o que eu passe para argc e argv;passando 0 e NULO resultados da mesma, assim como 1 e um VALOR definido para Qtrue.Tudo com argc/argv permanece como descrito.
Aqui está o código que eu estou trabalhando com:
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 internas não parecem ter muitos exemplos de rb_block_call com argc/argv...No máximo um ou dois, e acredito que todos eles simplesmente retransmitir os valores internamente, ao invés de usá-las.
Pensamentos?
Solução
Eu sou muito novo para Ruby C extensão, mas eu acho que a sua confusão.
VALUE rb_block_call(VALUE recv, ID mid, int argc, VALUE argv[],
VALUE (*func) (ANYARGS), VALUE data2)
argc/argv são aqui os argumentos para o Ruby função chamada.
C-função de chamada como um bloco:
VALUE block_function(VALUE rb_yield_value, VALUE data2, int argc, VALUE argv[])
argc/argv são os argumentos do bloco.
Um exemplo simples é injetar
Aqui é o C a tradução de:[1,2,3].injetar { |soma, e| soma + e }
#include "ruby.h"
static VALUE rb_puts(VALUE obj) {
return rb_funcall(rb_mKernel, rb_intern("puts"), 1, obj);
}
static VALUE inject_block(VALUE yield_value, VALUE data2, int argc, VALUE argv[]) {
printf("\nyield_value:\n");
rb_puts(yield_value);
printf("data2:\n");
rb_puts(data2);
printf("argc: %d\n", argc);
printf("argv:\n");
int i;
for(i = 0; i < argc; ++i) {
printf("argv %d:\n", i);
rb_puts(argv[i]);
}
VALUE sum = argv[0];
VALUE e = argv[1];// or yield_value
return INT2FIX(FIX2INT(sum) + FIX2INT(e));
}
static VALUE rb_block_call_test(int argc, VALUE argv[]) {
VALUE ary = rb_ary_new();
int i;
for(i = 0; i < 3; ++i) {
rb_ary_push(ary, INT2FIX(i+1));
}
VALUE block_argv[1];
block_argv[0] = INT2FIX(0);
ary = rb_block_call(ary,
rb_intern("inject"),
1, // argc
block_argv, //argv is a C-array of VALUE
inject_block,
Qtrue // data2
);
return ary;
}
void Init_rb_block_call() {
rb_define_global_function("rb_block_call_test", rb_block_call_test, 0);
}
que saídas (de uma chamada para rb_block_call_test):
yield_value: 0 # sum = argv[0]
data2: true
argc: 2
argv:
argv 0: 0 # sum
argv 1: 1 # e
yield_value: 1
data2: true
argc: 2
argv:
argv 0: 1
argv 1: 2
yield_value: 3
data2: true
argc: 2
argv:
argv 0: 3
argv 1: 3
# => 6
Eu acredito yield_value é sempre argv[0]
Se você deseja passar informações entre o bloco e o chamador e, em seguida, usar dados2
No seu exemplo, eu suponho #each_backtrace_frame está produzindo um "backtrace_frame" e para que é a razão argc/argv do bloco é sempre 1/the_backtrace_frame.Eu acredito #each_backtrace_frame aceita qualquer número de argumentos, pois não levantar qualquer erro quando você tentou passar.