Question

Je travaille avec ce qui devrait être une itération assez basique. Je comprends que je pouvais y accomplir avec du code de rubis, mais je travaille déjà dans une extension C. Je préférerais donc garder cette fonction en C avec le reste du code - surtout que ce devrait travail (d'une manière ou d'une autre) sans problème.

Le problème est avec rb_block_call. Voici comment Readme.ext décrit RB_BLOCK_CALL:

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

appelle une méthode sur le RECV, avec le Nom de la méthode spécifié par le symbole MID, fournissant du fonctionnement en tant que bloc. FUNC recevra la valeur du rendement comme premier argument, données2 comme le Deuxièmement, et argc / argv en tant que troisième / quatrième arguments.

Ainsi, ma compréhension (vérifiée en regardant Ruby Internals), est que la fonction de réception devrait ressembler à:

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

Et ici nous frappons notre problème. Dans mon cas d'utilisation (que je comprendrai ci-dessous), rb_yield_value et data2 sont adoptés comme prévu; Argc, d'autre part, est toujours réglé sur 1, argv [0] est rb_yield_value, argv [1] est faux, argv [2] est rb_yield_value, argv [3] jette une exception.

Peu importe ce que je passe pour argc et argv; Passer 0 et NULL résulte de la même manière, de même que 1 et une valeur définie sur Qtrue. Tout avec argc / argv reste comme décrit.

Voici le code que je travaille avec:

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 Ne semble pas avoir de nombreux exemples de rb_block_call avec Argc / Argv ... au plus un ou deux, et je crois qu'ils relient tous simplement les valeurs en interne plutôt que de les utiliser.

pensées?

Était-ce utile?

La solution

Je suis assez nouveau à ruby c extension, mais je pense où se trouve votre confusion.

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

argc / argv sont ici les arguments de la fonction Ruby que vous appelez.

dans la fonction c appelée comme bloc:

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

argc / argv sont les arguments du bloc.

Un exemple simple est injecter

Voici la traduction C de: [1,2,3] .Iject {| SUM, E |somme + 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);
}

Quelles sorties (d'un appel à 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

Je crois que céder_value est toujours argv [0]

Si vous souhaitez transmettre des informations entre le bloc et l'appelant, utilisez les données2

Dans votre exemple, je suppose que #each_backtrace_frame cédait un "Backtrace_frame" et c'est donc la raison pour laquelle argc / argv du bloc est toujours 1 / the_backtrace_frame.Je crois que #each_backtrace_frame accepte tout nombre d'arguments puisqu'il n'a soulevé aucune erreur lorsque vous avez essayé de passer certains.

Licencié sous: CC-BY-SA avec attribution
Non affilié à StackOverflow
scroll top