テンプレートを使用した再利用可能なアプローチがあります。これは、次のようなコードを記述できるinstall
およびメソッドに付属する派生型のジェネリック ファクトリです。create
int main() {
TypeVector<Base> t;
t.install<Foo>("Foo");
t.install<Bar>("Bar");
t.create("Foo")->hello();
}
これはスケッチの実装であることに注意してください。現実の世界では、別のテンプレート パラメーターを提供して、基になるコンテナーの型を指定する場合があります (いくつかの型でvector
は、おそらく よりも効率的ですset
)。
型ベクトルは次のとおりです。
template <typename Base>
class Creator;
template <typename Base>
class TypeVector {
public:
template <typename Derived>
void install (std::string const &name) ;
std::shared_ptr<Base> create (std::string const &name) const;
private:
struct Meta {
Meta(std::shared_ptr<Creator<Base>> creator, std::string const &name)
: creator(creator), name(name) {}
std::shared_ptr<Creator<Base>> creator;
std::string name;
};
std::vector<Meta> creators_;
};
どういうわけか、型を割り当て可能な方法で格納する方法が必要です。のようboost::shared_ptr
に行います。これは、抽象基本クラスとテンプレート派生クラスを組み合わせたものです。
template <typename Base>
class Creator {
public:
virtual ~Creator() {}
virtual std::shared_ptr<Base> create() const = 0;
};
template <typename Base, typename Derived>
class ConcreteCreator : public Creator<Base> {
public:
virtual std::shared_ptr<Base> create() const {
return std::shared_ptr<Base>{new Derived()};
}
};
「具体的なクリエーター」は、実際のオブジェクトを割り当て、そのベースへのポインターを返すことができます。
最後に、 と の実装を次に示しTypeVector::install
ますTypeVector::create
。
template <typename Base>
template <typename Derived>
void
TypeVector<Base>::install (std::string const &name)
{
creators_.emplace_back(
std::shared_ptr<Creator<Base>>(new ConcreteCreator<Base, Derived>()),
name);
}
template <typename Base>
std::shared_ptr<Base>
TypeVector<Base>::create (std::string const &name) const
{
for (auto m : creators_) {
if (name == m.name) return m.creator->create();
}
throw std::runtime_error("...");
}
そして最後に、ここにテストがあります:
#include <iostream>
struct Base {
virtual ~Base() {}
virtual void hello() const = 0;
};
struct Foo : Base {
virtual void hello() const { std::cout << "I am a Foo\n"; }
};
struct Bar : Base {
virtual void hello() const { std::cout << "I am a Bar\n"; }
};
int main() {
TypeVector<Base> t;
t.install<Foo>("Foo");
t.install<Bar>("Bar");
t.create("Foo")->hello();
}
さらに進んで、次のようなコードに対して任意のコンストラクターを呼び出し可能にすることができます...
...
Bar(Color, Age, int)
...
t.create("Foo", Color::Red, Age::TooOld, 42)
...しかし、これには、可変個引数のテンプレート引数リストと、それらをコンストラクター呼び出しに折りたたむ方法をよく理解する必要があります(実行可能であり、実行されていますが、この回答が爆発するでしょう)。