Pregunta

Estoy trabajando con lo que debería ser una iteración bastante básica. Entiendo que podría lograrlo con el código de Ruby, pero ya estoy trabajando en una extensión C, por lo que preferiría mantener esta función en C con el resto del código, especialmente porque este debería trabajo (de una forma u otra) sin problemas.

El problema es con rb_block_call. Aquí está la forma en que Readme.ext describe RB_BLOCK_CALL:

VALUE rb_block_call(VALUE recv, ID mid, int argc, VALUE * argv,
          VALUE (*func) (ANYARGS), VALUE data2)

llama un método en la REVV, con el Nombre del método especificado por el símbolo MID, suministrando funcionamiento como el bloque. FUNC recibirá el valor del rendimiento. como el primer argumento, Data2 como el Segundo, y ArgC / ARGV como el Tercero / cuarto argumentos.

Entonces, mi comprensión (verificada al mirar a Ruby Internals), es que la función de recepción debe parecer:

VALUE function( VALUE rb_yield_value, VALUE data2, int argc, VALUE argv );

y aquí golpeamos nuestro problema. En mi caso de uso (que incluiré a continuación), RB_YIELD_VALUE y DATA2 se pasan como se espera; Argc, por otro lado, siempre se establece en 1, Argv [0] es RB_YIELD_VALUE, ARGV [1] es FALSO, ARGV [2] es RB_YIELD_VALUE, ARGV [3] lanza una excepción.

No importa lo que pase por ARGC y ARGV; Pasando 0 y resultados nulos iguales, al igual que 1 y un valor establecido en QTRUE. Todo con ArgC / ARGV permanece como se describe.

Aquí está el código con el que estoy trabajando:

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 No parece tener muchos ejemplos de RB_BLOCK_CALL con ARGC / ARGV ... a la mayoría de uno o dos, y creo que todos simplemente transmiten los valores internamente en lugar de usarlos.

pensamientos?

¿Fue útil?

Solución

Soy bastante nuevo en la extensión RUBY C, pero creo que es tu confusión.

VALUE rb_block_call(VALUE recv, ID mid, int argc, VALUE argv[],
    VALUE (*func) (ANYARGS), VALUE data2)

argc / argv están aquí los argumentos a la función de rubí que llamas.

en la función C llamada como un bloque:

VALUE block_function(VALUE rb_yield_value, VALUE data2, int argc, VALUE argv[])

argc / argv son los argumentos del bloque.

Se inyecta un ejemplo simple

Aquí está la traducción de C: [1,2,3] .ijarR {| SUM, E |suma + 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);
}

qué salidas (de una llamada a 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

Creo que Rendimiento_Value siempre es Argv [0]

Si desea pasar información entre el bloque y la persona que llama, luego use datos2

En su ejemplo, supongo que #each_backtrace_frame está produciendo uno "backtrace_frame" y, por lo que es la razón por la que Argc / Argv del bloque es siempre 1 / the_backtrace_frame.Creo que #each_backtrace_frame acepta cualquier número de argumentos, ya que no planteó ningún error cuando intentó pasar algunos.

Licenciado bajo: CC-BY-SA con atribución
No afiliado a StackOverflow
scroll top