2010-07-10 4 views
2

나는 꽤 기본적인 반복 작업을해야한다. Ruby 코드로 구현할 수 있다는 것을 이해하지만 C 확장자로 이미 작업하고 있으므로 나머지 코드와 함께이 함수를 유지하는 것이 좋습니다. 특히 이 작동해야합니다 (편도 또는 다른) 문제없이.루비 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) 

블록으로 공급 FUNC 심볼 중간에 지정된 방법 이름의 RECV상의 메소드를 호출한다. func은 첫 번째 인수로 , 두 번째 인수로 , 두 번째 인수로 으로 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 확장 기능에 익숙하지 않지만 혼란이있는 곳에서 생각합니다.

여기 argc/argv는 사용자가 호출하는 Ruby 함수의 인수입니다. 블록으로 불리는 C 함수에서

:

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

는 argc/argv를 블록의 인수이다. [1,2,3]

간단한 예 C의 변환이 여기

를 주입한다.inject {| sum, e | (rb_block_call_test에 대한 호출)를 출력 합 + 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); 
} 

:

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 

I는 yield_value 항상 판단 argv를 [0]

는 블록들 간의 정보를 전달하려면 그리고 호출자는 다음 데이터 2를 사용하십시오

예를 들어, #each_backtrace_frame은 "backtrace_frame"을 하나 생성한다고 가정합니다. 따라서 블록의 argc/argv는 항상 1/the_backtrace_frame. 나는 #each_backtrace_frame이 어떤 인자를 넘겨 줬을 때 어떤 에러도 발생시키지 않았기 때문에 인자를 받아들이는 것으로 믿는다.

+0

확인 이것은 실제로 검토 할 때 의미가 있습니다. 문서는 argc/argv가 func (매개 변수)에 전달되고 mid에 의해 지정된 Ruby 메서드로 전달되지 않는다고 말하면서 설명서 버그로 판명되었습니다. 설명서는 다음과 같아야합니다. 기호 mid로 지정된 메서드 이름을 사용하여 recv에서 메서드를 호출하고 argv에서 argc 인수를 사용하여 func을 블록으로 제공합니다. func가 블록으로 호출되면 yield에서 첫 번째 인수로값을받습니다. 두 번째 인수는 data2입니다. – Asher

+0

... argc/argv는 세 번째/네 번째 인수로 사용되며 블록의 결과 값인 – eregon

+0

입니다. argc/argv는 내부적이며 API를 처리 할 때 참조하기에는 적합하지 않습니다. API에 대한 설명이 그 문언에서 잘못되었습니다. 설명서에서 참조하는 argc/argv는 내부 rb_yield가 아니라 rb_block_call을 참조합니다. 설명서는 기껏해야 혼란 스럽지만 제 생각에는 아주 분명하고 간단합니다. 업데이트해야합니다. – Asher