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か、あるいは……。
Subscribe to:
Post Comments (Atom)
No comments:
Post a Comment