Automatic compilation factory registration of class templates in C ++

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?

+8
c ++ initialization static templates factory-pattern
source share
2 answers

I sent an answer to a similar problem in GameDev, but the solution is not compilation time. You can check it out here:
> https://gamedev.stackexchange.com/questions/17746/entity-component-systems-in-c-how-do-i-discover-types-and-construct-components/17759#17759

I don’t think there is even a way to do this compilation time. Your "id" inside the base class is just a simplified form of RTTI, which by definition is the runtime. Maybe if you made id the template argument ... but that would make some other things a lot more complicated.

+3
source share

Doing simpler things will shrink and make your intentions obvious.

 int main() { RegisterConcreteTypeFoo(); RegisterConcreteTypeBar(); // do stuff... CleanupFactories(); return 0; } 

When these init functions are actually called (and not at compile time) and they fail, you will not get all the pretty simple things that make debugging easier. Like a stack trace.

In this case, you also assume that you do not want to initialize them differently. It’s too difficult to unit test “automatically register” anything, for example.

Less magic = easier, cheaper.

If this was not enough, there are technical problems. Compilers love to extract unused characters from libraries. Maybe some specific shantanist for the compiler to get around it, I'm not sure. I hope he does this sequentially, instead of randomly, for no apparent reason, in the middle of the development cycle.

0
source share

All Articles