代码之家  ›  专栏  ›  技术社区  ›  Asher postmodern

Ruby 1.9.1-p378 C扩展rb\u block\u调用怪异

  •  2
  • Asher postmodern  · 技术社区  · 15 年前

    我的工作应该是一个相当基本的迭代。我知道我可以用Ruby代码来完成它,但是我已经在C扩展中工作了,所以我更愿意把这个函数和其他代码一起保存在C中——特别是因为 应该

    问题是rb\u block\u call。下面是README.EXT如何描述rb\u block\u调用:

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

    由符号指定的方法名称 func将从yield接收值 作为第一个参数,data2作为 第三/第四个参数。

    因此,我的理解(通过查看Ruby内部结构验证)是,接收函数应该如下所示:

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

    现在我们来谈谈我们的问题。在我的用例中(我将在下面包括),rb\u yield\u value和data2按预期传递;另一方面,argc总是设置为1,argv[0]是rb\u yield\u value,argv[1]是false,argv[2]是rb\u yield\u value,argv[3]引发异常。

    我认为argc和argv是什么并不重要;传递0和NULL的结果与传递1和设置为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内部似乎没有很多使用argc/argv的rb\u block\u调用的例子。。。最多一两个,我相信它们都只是在内部传递值,而不是使用它们。

    1 回复  |  直到 13 年前
        1
  •  4
  •   eregon    15 年前

    我对rubyc扩展相当陌生,但我想你的困惑在哪里。

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

    argc/argv是您调用的Ruby函数的参数。

    在称为块的C函数中:

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

    argc/argv是块的参数。

    一个简单的例子是inject

    以下是:[1,2,3]的C翻译。inject{sum,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);
    }
    

    (对rb\U块调用\U调用\U测试的)输出:

    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
    

    如果您想在块和调用者之间传递信息,那么使用data2

    在您的示例中,我假设每个#backtrace#帧产生一个“backtrace#帧”,这就是为什么块的argc/argv总是1/backtrace#帧。我相信#每个#回溯#帧接受任意数量的参数,因为当您尝试传递一些参数时,它不会引发任何错误。