Ruby と C API: グローバル メソッドの rb_funcall_with_block? Cからブロックを使用してRubyグローバルメソッドを呼び出すにはどうすればよいですか?
概要
C API コードから Ruby のグローバル メソッドを呼び出したいと考えています。したがって、Rubyでは次のようになります。
def giveMeABlock(*args)
puts "Starting giveMeABlock with #{args.inspect}"
yield if block_given?
end
その後学んだことですが、グローバル関数は実際には Object の単なるプライベート関数であるため、どこからでも呼び出すことができます。
C ではこのメソッドを呼び出したいので、 rb_funcallv を使用できます。
VALUE rb_funcallv(VALUE recv, ID mid, int argc, VALUE *argv)
Invokes a method, passing arguments as an array of values. Able to call even private/protected methods.
この特定の例では、次のことができます。
rb_funcallv(self, rb_intern("giveMeABlock"), 0, NULL);
また、ブロックが指定されていないにもかかわらず、メソッドを呼び出すことができます。
ブロックを使用して呼び出すには、次のようにします。
VALUE rb_funcall_with_block(VALUE recv, ID mid, int argc, const VALUE *argv, VALUE passed_procval)
Same as rb_funcallv_public, except passed_procval specifies the block to pass to the method.
ただし、これは rb_funcallv_public と同様、パブリック メソッドのみを呼び出すことができます。つまり、試してみると:
rb_funcall_with_block(self, rb_intern("giveMeABlock"), 0, NULL, block);
わかりました:
private method `giveMeABlock' called for main:Object (NoMethodError)
では、なぜブロックを提供できるプライベート メソッドの funcall がないのでしょうか、それとも何かが足りないのでしょうか?そして、この一見単純なタスクを達成するにはどうすればよいでしょうか?
Object クラス内でメソッドを定義すると、それが機能することがわかりました (パブリックになっているので)。しかし、これはハックのようで、Ruby ソースを変更できることが前提になっています。
解決策
パーティーに遅れてしまったので、おそらくこの回答はもう必要ないでしょうが、後世のために投稿しておきます。
私が見つけたこの問題の回避策は、関数をパブリックにすることでした。 Ruby コードから直接実行することもできます。
public def giveMeABlock(*args)
puts "Starting giveMeABlock with #{args.inspect}"
yield if block_given?
end
または、それができない場合 (たとえば、実行する必要がある Ruby コードを変更できない場合)、C コードからコードを公開できます。
rb_funcall(rb_cObject, rb_intern("public"), 1, rb_id2sym(rb_intern("giveMeABlock")))
これでパブリックになったので、rb_funcall_with_block で呼び出すことができます。