トップ «前の日記(2010-05-10) 最新 次の日記(2010-05-12)» 編集

日々の破片

Subscribe with livedoor Reader
著作一覧

2010-05-11

_ &block続き

blockをC APIで呼び出すの続き。

元の問題は、

class A
  def a(&proc)
    @proc = proc
  end
  def run_a(himself)
    himself.instance_eval &@proc
  end
end

の&procをどうすればC APIで実現できるかということだった。

上のaに相当するコードは割りと簡単にわかった。

if (rb_block_given_p())
{
    rb_ivar_set(self, rb_intern("@proc"), rb_block_proc());
}

が、この後がいけない。上で@procに保存したのはProcのインスタンスだが、Methodと異なり、元のselfが組み込まれていてそれを外せない。したがって、元のself以外のselfのコンテキストで実行することができない。

eval.cを読むと、rb_proc_callにはselfは与えられないし(Qundefが設定される)、与えられるproc_invokeはstaticだ。

逆に考えて、元のスクリプト同様にブロックを用意するのはどうだろうか?

PUSH_BLOCKしかないのかな?

では、&はどうやっているのかと思うと、NEW_BLOCK_PASSになるようで(合ってるかな?)つまるところノードに評価時点で組み込まれてしまう。するとeval.cのblock_passに入ってくるので、この関数のstaticを外して、と考えてみるまでも無くノードが引数なわけだしそれは外部に公開されていないはずだ。

で、考えるのをやめて、

rb_ivar_set(himself, rb_intern("@_temporary_block"), proc);
rb_funcall(himself, rb_intern("instance_eval"),
  1, rb_str_new2("instance_eval &@_temporary_block"));

とした。というわけで、現在のところ、&argに相当するC APIは無いでFAなのかなぁ。

VALUE rb_block_pass(VALUE self, VALUE proc);
selfをレシーバとしてprocをブロックとして与える。

rb_block_pass(self, proc);
if (!rb_block_given_p()) {
  rb_raise("bug");
}
rb_funcall(self, rb_intern("instance_eval"), 0, 0);

という感じのが欲しいような気もするが、おそらく公開APIにするよりは、インタプリタを使わせるほうが良いのだろうな。


2003|06|07|08|09|10|11|12|
2004|01|02|03|04|05|06|07|08|09|10|11|12|
2005|01|02|03|04|05|06|07|08|09|10|11|12|
2006|01|02|03|04|05|06|07|08|09|10|11|12|
2007|01|02|03|04|05|06|07|08|09|10|11|12|
2008|01|02|03|04|05|06|07|08|09|10|11|12|
2009|01|02|03|04|05|06|07|08|09|10|11|12|
2010|01|02|03|04|05|06|07|08|09|10|11|12|
2011|01|02|03|04|05|06|07|08|09|10|11|12|
2012|01|02|03|04|05|06|07|08|09|10|11|12|
2013|01|02|03|04|05|06|07|08|09|10|11|12|
2014|01|02|03|04|05|06|07|08|09|10|11|12|
2015|01|02|03|04|05|06|07|08|09|10|11|12|
2016|01|02|03|04|05|06|07|08|09|10|11|12|
2017|01|02|03|04|05|06|07|08|09|10|11|12|
2018|01|02|03|04|05|06|07|08|09|10|11|12|
2019|01|02|03|04|05|06|07|08|

ジェズイットを見習え