これも Factory 同様非常に簡単です。 ID の代わりに RTTI を使う Factory です。 いちいちクラスごとに ID をふる手間は省けますが、 今度は他のトレードオフがあるのです。
class A { public: virtual void print() =0; virtual ~A() {} }; class B : public A { public: virtual void print() { std::cout << "B" << std::endl; } }; class C : public A { public: virtual void print() { std::cout << "C" << std::endl; } };
状況は Factory と同じ、こんなクラス群から、B, C が作りたいのです。 今度の準備は…
template <class _New> A* newA(const A* a) { return new _New(dynamic_cast<const _New>(*a)); } typedef Loki::CloneFactory<A> AFactory;
何が変ったか、って言いますと、まず、ID を表わす const int が消えました。 これが嬉しいところですね。 それで、newA がデフォルトコンストラクタでは無く、 コピーコンストラクタを呼びだすようになっています。 これが今回の肝。 それでまたもおなじみ、ポリシーを設定しての typedef。 今回は A を返しますよ、とだけ Factory に教える。
んで、使いかたはというと、まずクラスを登録して、
AFactory factory; factory.Register(typeid(B), newA<B>); factory.Register(typeid(C), newA<C>);
クローンファクトリなので、クローン元となるオブジェクトが必要です。
A* b = new B; A* c = new C;
それで、実際にオブジェクトを生成します。
std::vector<A*> vec; vec.push_back(factory.CreateObject(b)); vec.push_back(factory.CreateObject(c)); vec.push_back(factory.CreateObject(b)); // should print "BCB" std::for_each(vec.begin(), vec.end(), std::mem_fun(&A::print));
これもちゃんと "BCB" と出力されます。
Factory より、こっちの方が煩雑な ID 管理をしなくて良いため、ありがたいのです。 折角の RTTI、あるんなら生かさにゃ損というものです。
ただ、前回述べた通り、クライアントコードに対して、 前回の ID とはまた別の制限をかけたことになります。 生成元となるオブジェクトが必要である、というのがその制限です。 つまり、ファイルからシリアライズされているオブジェクトを 復元する目的では使えないのです。元になるオブジェクトがありませんから。
さて、前回同様他のコンポーネントの組合せの話を最後に。 生成関数に Functor を使うお話です。 使い方は簡単、typedef 文を以下のように変更するだけ。
typedef Loki::Functor<A*, TYPELIST_1(const A*)> Func; typedef Loki::CloneFactory<A, Func> AFactory;
こうするだけで、関数ポインタだけでなく、 ありとあらゆる関数ライクな挙動をするものを渡せるようになったのです。 まあ、どんなものでも渡せているのは Functor の項を参照のこと。
と、いうのが建前なんですが、実はこれ、コンパイルが通りません。 ソース中にも書きましたが、loki/Factory.h の 122行目は、 const_iterator では無く iterator を使うべきだと思うのです。 これを修正すれば、通ります。
全てリンクフリーです。 コード片は自由に使用していただいて構いません。 その他のものはGPL扱いであればあらゆる使用に関して文句は言いません。 なにかあれば下記メールアドレスへ。