2007-02-24

I can read! I CAN READ IT!

 http://d.hatena.ne.jp/mb2sync/20050324  読める、読めるぞ!  しかし、これは複雑だ。確かに仮想関数はコストが高いとはいえ、ここまでやるとは。  とりあえず、このソースコードが何をしているか記述してみる。  まず、boost::functionのテンプレート引数に関数の型を渡す。いわば、関数のシグネチャだ。この型によって、然るべきfunctionの特殊化が選ばれる。さて、関数の呼び出し方は分かった。しかし、これだけではまだ足りない。代入される関数オブジェクトの型は、ひとつではないのだ。そもそも、関数オブジェクトには、二種類ある。クラスのオブジェクトとしての関数オブジェクトと、単なる関数ポインタだ。この二つの場合分けをしなければならない。そのため、コンストラクタはテンプレート関数になる。そして、get_function_tagに、代入された型を渡す。このメタ関数は、関数ポインタかどうかを判断してくれる。コンパイル時の条件分岐には、いくつか方法があるが、このコードでは、Overload Resolutionを利用して条件分岐している。  関数オブジェクトの場合、function_obj_managerのstaticメンバ関数を利用して、関数呼び出し時(invoker)と、破棄時(destroyer)の処理を決定する。そして、関数オブジェクトをコピーし、ポインタをvoid *型にキャストしておく。  関数ポインタの場合は、同じようにするが、ポインタは、void(*)()型にキャストする。これは規格上、関数ポインタとvoid *間のキャストができないためだ。ポータブルな実装というべきか。  さて、いよいよ関数を呼び出す。operator()();は、先のポインタと、引数をそのままinvokerへと渡し、戻り値があれば、返すだけだ。invokerは、関数オブジェクトか関数ポインタかによって、元ポインタを、適切な型にキャストして、呼び出す。  破棄されるときには、destroyerが呼び出される。関数オブジェクトならば、deleteし、関数ポインタならば、なにもしない。  しかしまあ、よくもこれを実装しようと思ったものだ。次の強敵は、bindだ。ところでラスボスはなんだろう。Expression Templateを使った変態的なSpiritか、Xpressiveか、あるいは……。

No comments: