長くなっちゃったのでこっちに書きました.
と言っても以下の文章のほとんどはhttp://www.cdiggins.com/bil.htmlに書いてあることそのまんまなんですが・・・.
>なかなか使いどころが難しいのですが
とりあえず,(旧来の)インターフェースというものがあったところにはおおよそ使えると思います.自分としては(C++における)OOPの基本的な部分を変えうる可能性を持ってそうな気はするんですが,どうなんでしょうか?<自分もよく分かってない
以下,Boost.Interfaceによる「インターフェース」の特徴,特に従来のC++における抽象基底クラスやJavaにおけるInterface(実はこっちをあまりよく知らないという・・・)によるインターフェースとの比較における特徴を挙げておきます.
- 抽象化の側面・粒度に関する意思決定を遅延できる(後書きできる)
最初にフラットにクラス群をポコポコ書いておきます.ただしメンバ関数のシグネチャだけは合わせておきます.要するにあるシンタックスが通る(コンパイルが出来る)という静的多相性は用意しておいて,
class Triangle { public: void draw() const; void moveTo(double x, double y); double getX() const; double getY() const; }; class Rectangle { public: void draw() const; void moveTo(double x, double y); double getX() const; double getY() const; };
そのクラス群に対して
// 「draw()を持ったもの」として扱いたくなった BOOST_INTERFACE_BEGIN(Drawable) BOOST_INTERFACE_CONST_FUNCTION0(draw, void) BOOST_INTERFACE_END(Drawable) // 「動かせるもの」として抽象的に扱いたい BOOST_INTERFACE_BEGIN(Movable) BOOST_INTERFACE_FUNCTION2(moveTo, void, double, double) BOOST_INTERFACE_END(Movable) // そ〜言えば「動かせるもの」かつ「座標を取得できるもの」としても扱いたいにゃぁ BOOST_INTERFACE_BEGIN(MoveAndGetPoint) BOOST_INTERFACE_FUNCTION2(moveTo, void, double, double) BOOST_INTERFACE_CONST_FUNCTION(getX, double) BOOST_INTERFACE_CONST_FUNCTION(getY, double) BOOST_INTERFACE_END(MoveAndGetPoint)
上のように任意の側面・粒度での抽象化が「後出しで」可能になる,という感じでしょうか.
- シンタックス志向である
対象となるメンバ関数がvirtualだろうがnon-virtualだろうがstaticだろうがとりあえず
obj.func();
という構文が通るものなら何にでも動的多相性を付与できるって感じでしょうか.『Modern C++ Design』でいうところの「シンタックス志向における制限の緩さ」(日本語版ではp9のとこ参照)が活きてくるというか.
- vptrによる空間的オーバーヘッドが必要な場合にだけ限られる
抽象基底クラスから派生したクラスのオブジェクトには(一般的な実装では)vptrによる空間的オーバーヘッドが常に付いて回ります.これは動的多相性が必要ないところでは本当にただ無駄なだけです.Boost.Interfaceによる動的多相性の後付けでは,それが本当に必要なときにだけオーバーヘッドが発生します.この利点はiteratorのような空間的に小さなオブジェクトでは特にクリティカルだと思います(実際,boost::any_iteratorという提案があります).
- inline化出来るところではinline化してくれる
仮想関数の呼び出しは,コンパイル時に型が決まっていそうな場所でも,参照やポインタ経由で呼び出すとまずinline化されないです.
class Abst { public: virtual void f() const = 0; }; class C : public Abst { public: virtual void f() const; }; g(Abst const &a) { a.f(); } Abst *pa = new C; g(*pa); // ここでのf()の呼び出しはinline化できなくて当たり前 C c; g(c); // こっちでのf()の呼び出しはinline化してくれてもよさそうだけれど, // おそらくされない
それに対して,Boost.Interfaceによる動的多相性の後付けだとinline化可能な場所ではinline化してくれるようなコードを書くことが可能です(ちなみに上のコードでgをテンプレートにしても結果は変わらないはず).
class C { public: void f() const; }; BOOST_INTERFACE_BEGIN(Abst) BOOST_INTERFACE_CONST_FUNCTION0(f, void) BOOST_INTERFACE_END(Abst) template<class AbstT> g(AbstT const &a) { a.f(); } Abst a(*(new C)); // 簡単のためなのでここに対するツッコミはご容赦を・・・ g(a); // ここでのf()の呼び出しはやはりinline化できなくて当たり前 C c; g(c); // こちらのf()の呼び出しはinline化される(かも)
- バインディング時期の変更が容易になる?
これは自分が勝手に考えていることであんまりまとまってないんですが・・・.
本来静的多相性のみを予定していたところに動的多相性を持ち込みたくなったとか,あるいは静的多相性と動的多相性の切り替えがしたい,ってときにそれが柔軟に可能になる,みたいな.こちらはバインディング時期の意思決定すら遅延させられるって考えられるんでしょうか?う〜,こっちはちょっと良い具体例が思い付きません・・・.無理に考えるなら例えばコールバックとかでしょうか.コールバックとしてテンプレートで任意の型を受け付けるようにしておいて,ファンクタが来ればコンパイル時決定でinline化可能で効率的で(゜д゜)ウマーで,一方でboost::function(これもBoost.Interfaceで言うところの「インターフェース」だと思います)が来れば実行時にコールバックを切り替えられて柔軟で(゜д゜)ウマー,みたいな感じで考えてはいるんですが・・・.
自分にもう少し広い視野があれば,他のプログラミング言語の特定の機能などと比較したりなんかしてより正確な分析が出来たんでしょうけれど・・・っていうか誰かもっとしっかりした分析が出来る人が解説してくれにゃいでしょうか?
あ〜,またマルチパラダイムデザインあたりを読み直さなくちゃいけない気もします・・・.
>boost::serializationのserialize関数みたく動的多様性のあるクラスに
>静的多様性しかないファンクションテンプレートを追加する場合には威力を発揮するのかな?。
うぅ,また面白そうなことを・・・.serializationはヒマがないのでまだちゃんと遊んだり実装読んだりしてません・・・.余裕が出来たらそのあたりは是非見てみます.