Domanda

Sto lavorando con ciò che dovrebbe essere una iterazione abbastanza semplice. Capisco che potrei realizzarlo con il codice rubino, ma sto lavorando già in un'estensione C, quindi preferirei mantenere questa funzione in C con il resto del codice, specialmente dal momento che dovrebbe (un modo o un altro) senza problemi.

Il problema è con rb_block_call. Ecco come readme.ext descrive rb_block_call:

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

chiama un metodo sul recv, con il Nome del metodo specificato dal simbolo Mid, fornendo func come il blocco. FUNC riceverà il valore dalla resa Come primo argomento, Data2 come il Secondo, e ARGC / ARGV come il Terzo / quarto argomento.

Quindi, la mia comprensione (verificata osservando gli interni Ruby), è che la funzione ricevente dovrebbe essere simile a:

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

E qui abbiamo colpito il nostro problema. Nel mio caso d'uso (che includerò di seguito), rb_yield_value e dati2 sono passati come previsto; ARGC, d'altra parte, è sempre impostato su 1, argv [0] è rb_yield_value, argv [1] è falso, argv [2] è rb_yield_value, argv [3] genera un'eccezione.

Non importa ciò che passo per ARGC e ARGV; Passare 0 e risultati NULL Lo stesso, come fa 1 e un valore impostato su QTRUE. Tutto con ARGC / ARGV rimane come descritto.

Ecco il codice con:

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;
}
.

Gli interni rubini non sembrano avere molti esempi di rb_block_call con ARGC / ARGV ... al massimo uno o due, e credo che tutti semplicemente trasmettono i valori internamente piuttosto che usandoli.

Pensieri?

È stato utile?

Soluzione

Sono abbastanza nuovo per l'estensione rubino c, ma penso dove sia la tua confusione.

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

ARGC / ARGV sono qui gli argomenti della funzione Ruby che chiami.

Nella funzione C denunciata come blocco:

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

ARGC / ARGV sono gli argomenti del blocco.

Un semplice esempio è iniettato

Ecco la traduzione c di: [1,2,3] .inject {| somma, e |SUM + 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);
}
.

quali uscite (di una chiamata 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
.

credo che il resa_value sia sempre argv [0]

Se si desidera passare le informazioni tra il blocco e il chiamante, quindi utilizzare DATA2

Nell'esempio, suppongo #Each_backtrace_frame sta producendo un "backtrace_frame" e così è il motivo per cui ARGC / ARGV del blocco è sempre 1 / the_backtrace_frame.Credo #_backtrace_frame accetta un numero qualsiasi di argomenti poiché non ha sollevato alcun errore quando hai provato a passare un po '.

Autorizzato sotto: CC-BY-SA insieme a attribuzione
Non affiliato a StackOverflow
scroll top