Factory パターンへのさまざまなアプローチをオンラインで検索した結果、非常に満足している独自のバージョンを実装しました。Registerメンバー関数は、テンプレート型の関数ポインターを作成し、インデックスをキーとして使用してstd::mapに格納します。以下のコードは問題なくコンパイルおよび実行されます (Windows 7 64 ビット、Code::Blocks 10.05 with GCC)。
#ifndef OBJECT_FACTORY_HPP
#define OBJECT_FACTORY_HPP
#include <map>
namespace internal {
template<typename BaseType, typename ObjectType>
BaseType* CreateFunction() {
return new ObjectType;
}
}
template<typename BaseType, typename IndexType>
class ObjectFactory {
public:
ObjectFactory();
template<typename ObjectType>
bool Register(const IndexType& index);
bool Unregister(const IndexType& index);
BaseType* Create(const IndexType& index);
private:
typedef BaseType* (*creationCallback)();
typedef std::map<IndexType, creationCallback> Registry;
Registry registry;
// private:
// template<typename ObjectType>
// BaseType* CreateFunction();
};
template<typename BaseType, typename IndexType>
ObjectFactory<BaseType, IndexType>::ObjectFactory() {
registry.clear();
}
template<typename BaseType, typename IndexType>
template<typename ObjectType>
bool ObjectFactory<BaseType, IndexType>::Register(const IndexType& index) {
if (registry.find(index) != registry.end())
return false;
registry[index] = &internal::CreateFunction<BaseType, ObjectType>;
// registry[index] = &CreateFunction<ObjectType>; <-- FAILS!
return true;
}
template<typename BaseType, typename IndexType>
bool ObjectFactory<BaseType, IndexType>::Unregister(const IndexType& type) {
if (registry.find(type) == registry.end())
return false;
return (registry.erase(type) == 1);
}
template<typename BaseType, typename IndexType>
BaseType* ObjectFactory<BaseType, IndexType>::Create(const IndexType& index) {
if (registry.find(index) == registry.end())
return NULL;
return registry[index]();
}
//template<typename BaseType, typename IndexType>
//template<typename ObjectType>
//BaseType* ObjectFactory<BaseType, IndexType>::CreateFunction() {
// return new ObjectType();
//}
#endif
私の最初のアプローチは、CreateFunctionをプライベート メンバーとしてユーザーから非表示にすることでした (コメント セクションを見てください。非メンバー関数は追加のテンプレート引数を取ることに注意してください)。ただし、これは次のエラー メッセージで失敗し、すべてRegisterメンバー関数に関数ポインターを格納する行を指しています。
In member function 'bool ObjectFactory<BaseType, IndexType>::Register(const IndexType&) [with ObjectType = Triangle, BaseType = Shape, IndexType = std::basic_string<char, std::char_traits<char>, std::allocator<char> >]':|
instantiated from here
error: no matches converting function 'CreateFunction' to type 'class Shape* (*)()'|
error: candidates are: template<class ObjectType> BaseType* ObjectFactory::CreateFunction() [with ObjectType = ObjectType, BaseType = Shape, IndexType = std::basic_string<char, std::char_traits<char>, std::allocator<char> >]|
テスト用に小さなテスト クライアントを作成しました。
#include "ObjectFactory.hpp"
#include <iostream>
class Shape {
public:
Shape() {}
virtual void print() { std::cout << "At heart, I'm a shape"; }
};
class Triangle : public Shape {
public:
Triangle() {}
void print() { Shape::print(); std::cout << ", but I'm truly a triangle" << std::endl; }
};
int main(int argc, char* argv[]) {
ObjectFactory<Shape, std::string> objectFactory;
objectFactory.Register<Triangle>("triangle");
Shape* triangle = objectFactory.Create("triangle");
triangle->print();
delete triangle;
return 0;
}
これは可能ですか?そうあるべきだと思いますし、どういうわけか間違ったメンバー関数を呼び出していることは理解していますが、その理由はわかりません。関連するメモとして、誰かが言及するかもしれないので、私は単純な new 演算子の代わりに割り当てに *boost::shared_ptr* を使用する予定です;) 実装に関するその他のアドバイスや提案も大歓迎です。