Ruby 1.9.1-P378 C拡張rb_block_call wearness.
-
13-09-2020 - |
質問
かなり基本的な反復であるべきことを扱います。私はRubyコードでそれを達成できることを理解していますが、私はすでにC拡張機能で働いているので、この関数をCでは、特にこのがの仕事をする必要があるので、この関数をCで保つことをお勧めします。問題なしの(ある方法で)。
問題は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でメソッドを呼び出します。 シンボルで指定されたメソッド名 中MID、ブロックとしてFUNCを供給します。 FUNCは歩留まりからの値を受け取ります 最初の引数として、DATA2としてのデータ2 第二に、ARGC / ARGV 第3/4回目の引数
だから、私の理解(Ruby Internalalsを見ることによって確認されている)は、受信機能が次のように見えるようになることです。
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と同じ値と同じ値が同じです。 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 Internalsは、argc / argvを持つRb_block_callの例が多いようには思われません。ほとんどの場合は、それらを使用するのではなく内部で値を内部的に中継すると思います。
思考?
解決
Ruby C Extensionにはかなり新しいですが、私はあなたの混乱がどこにあるのかと思います。
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] .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_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が1つの "backtrace_frame"をもたらしているので、ブロックのargc / argvが常に1 / the_backtrace_frameの理由です。#each_backtrace_frameは、渡したときにエラーが発生しなかったため、任意の数の引数を受け入れます。