I am looking for an abstract factory for class templates where classes are automatically registered at static initialization time. For regular (not templated) classes, the solution is simple enough using static elements. Here is an example of a (fairly simplified) solution that works just fine:
#include <cassert> #include <iostream> class Base { public: virtual size_t id() const = 0; virtual const char* name() const = 0; virtual ~Base() {} }; typedef Base* (*CreateFunc)(void); class SimpleFactory { private: static const size_t NELEM = 2; static size_t id_; static CreateFunc creators_[NELEM]; public: static size_t registerFunc(CreateFunc creator) { assert(id_ < NELEM); assert(creator); creators_[id_] = creator; return id_++; } static Base* create(size_t id) { assert(id < NELEM); return (creators_[id])(); } }; size_t SimpleFactory::id_ = 0; CreateFunc SimpleFactory::creators_[NELEM]; class D1 : public Base { private: static Base* create() { return new D1; } static const size_t id_; public: size_t id() const { return id_; } const char* name() const { return "D1"; } }; const size_t D1::id_ = SimpleFactory::registerFunc(&create); class D2 : public Base { private: static Base* create() { return new D2; } static const size_t id_; public: size_t id() const { return id_; } const char* name() const { return "D2"; } }; const size_t D2::id_ = SimpleFactory::registerFunc(&create); int main() { Base* b1 = SimpleFactory::create(0); Base* b2 = SimpleFactory::create(1); std::cout << "b1 name: " << b1->name() << "\tid: " << b1->id() << "\n"; std::cout << "b2 name: " << b2->name() << "\tid: " << b2->id() << "\n"; delete b1; delete b2; return 0; }
I have a question how to make it work when the material I want to register / create is more like:
template <typename T> class Base... template <typename T> class D1 : public Base<T> ...
The best idea I can think of is a factory pattern, something like:
template <typename T> class SimpleFactory { private: static const size_t NELEM = 2; static size_t id_; typedef Base<T>* Creator; static Creator creators_[NELEM]; ...(the rest remains largely the same)
But I am wondering if there is a better way, or if someone has already implemented such a template before.
EDIT: Revision of this problem after a few years (and with variational patterns), I can get closer to what I want by simply “registering” functions, or rather classes, as template parameters for the factory. It will look something like this:
#include <cassert> struct Base {}; struct A : public Base { A() { std::cout << "A" << std::endl; } }; struct B : public Base { B() { std::cout << "B" << std::endl; } }; struct C : public Base { C() { std::cout << "C" << std::endl; } }; struct D : public Base { D() { std::cout << "D" << std::endl; } }; namespace { template <class Head> std::unique_ptr<Base> createAux(unsigned id) { assert(id == 0); return std::make_unique<Head>(); } template <class Head, class Second, class... Tail> std::unique_ptr<Base> createAux(unsigned id) { if (id == 0) { return std::make_unique<Head>(); } else { return createAux<Second, Tail...>(id - 1); } } } template <class... Types> class LetterFactory { public: std::unique_ptr<Base> create(unsigned id) const { static_assert(sizeof...(Types) > 0, "Need at least one type for factory"); assert(id < sizeof...(Types)); return createAux<Types...>(id); } }; int main() { LetterFactory<A, B, C, D> fac; fac.create(3); return 0; }
Now this is just a simplified prototype, so never create () linear complexity. The main drawback of this design, however, is that it does not allow any constructor parameters. Ideally, I could register not only the classes that the factory should create, but also the types that each class accepts in its constructor, and let create () accept them variably. Has anyone ever done something like this before?