2010-07-10 60 views
2

我正在與什麼應該是一個相當基本的迭代。我知道我可以用Ruby代碼來完成它,但我已經在C擴展中工作,所以我寧願用C代碼保留這個函數 - 尤其是因爲這個應該工作(單向或者其他方式) )沒有問題。Ruby 1.9.1-p378 C擴展rb_block_call奇怪

問題在於rb_block_call。這裏是README.EXT如何描述rb_block_call:

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

呼籲對recv的方法,由符號 中期指定的 方法名,提供FUNC爲塊。 func將從yield 作爲第一個參數獲得值,data2作爲 秒,argc/argv作爲 第三個/第四個參數。

所以,我的理解(通過看紅寶石內部驗證),是接收函數應該是這樣的:

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

在這裏,我們打我們的問題。在我的用例中(我將在下面介紹),rb_yield_value和data2按預期傳遞;另一方面,argc始終設置爲1,argv [0]爲rb_yield_value,argv [1]爲false,argv [2]爲rb_yield_value,argv [3]引發異常。

無論我爲argc和argv傳遞什麼,傳遞0和NULL結果相同,1和VALUE設置爲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; 
} 

紅寶石內部似乎並沒有與ARGC/argv的rb_block_call的例子很多......最多一兩個,我相信他們都只需在內部傳遞值而不是使用它們。

想法?

回答

4

我對Ruby C擴展很新,但我認爲你的困惑在哪裏。

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的是該塊的參數。

一個簡單的例子是注入

這裏是C的翻譯:[1,2,3]。注入{| sum,e |總和+ 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_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 

相信yield_value總是ARGV [0]

如果要在塊之間傳遞信息和來電者,然後使用data2

在你的例子中,我想#each_backtrace_frame正在產生一個「backtrace_frame」,所以這是該塊的argc/argv的原因總是1/the_backtrace_frame。我相信#each_backtrace_frame可以接受任意數量的參數,因爲當您嘗試傳遞一些參數時它不會引發任何錯誤。

+0

確定這實際上是有意義的審查。這確實是一個文檔錯誤,因爲文檔說argc/argv將被傳遞給func(這是一個參數),而不是中間指定的Ruby方法。文檔應爲: 在recv上調用方法,方法名稱由 符號指定,argc參數在argv中,提供func作爲塊。當func作爲塊被調用時,它將從yield作爲第一個參數接收 值,並將data2作爲第二個參數。 – Asher 2010-07-11 10:20:13

+0

...和argc/argv作爲第三/四個參數,作爲塊的生成值 – eregon 2010-07-11 10:40:15

+0

argc/argv是內部的,在我們解決API時沒有意義引用。 API文檔的措辭不正確。文檔引用的argc/argv引用了rb_block_call,而不是內部的rb_yield。該文件充其量是令人困惑的,但在我看來相當明顯和簡單的錯誤。它應該更新。 – Asher 2010-07-12 04:51:51