For a long time now I have wanted to make a design whereby classes would call functions by initializing global variables so as to add themselves to a global factory of constructible classes, thus avoiding writing and maintaining a large list of classes manually - it would all be automatic as the classes could self-register.
However for the longest time I could not solve the problem of the global factory possibly not being initialized before any of the classes try to register themselves. This is an inherent problem with global variables, and just one of many reasons they are avoided.
Then I found out that static variables in functions are initialized the first time the function is called, and an idea struck - no matter what order the classes register in, if the global factory is a static variable in a function that returns it, it will always be initialized before it needs to be used. Problem solved!
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19
|
struct GlobalFactory
{
using COnstructor_t = std::function<std::unique_ptr<Base> (Blah, Meh)>;
using Factory_t = std::map<std::string /*ID*/, Constructor_t>;
using Registration_t = Factory_t::const_iterator;
static Registration_t Register(std::string ID, Constructor_t constructor)
{
Factory()[ID] = constructor;
return Factory().find(ID);
}
private:
static Factory_t &Factory()
{
static Factory_t factory;
return factory;
}
};
|
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18
|
struct ClassA : Base
{
//...
private:
static GlobalFactory::Registration_t registration;
};
//cpp file:
GlobalFactory::Registration_t ClassA::registration = GlobalFactory::Register
(
"MyCode::ClassA",
[](Blah b, Meh m) -> std::unique_ptr<Base>
{
//I don't think I can use make_unique here
return std::unique_ptr<Base>{new ClassA{b, m}};
}
);
|
I haven't tested this code, but after fixing any syntax errors it should work, even with ClassB, ClassC, ClassD, etc...
The whole point of this is to avoid maintaining a list of all your classes - just compile them into the project or load them from a DLL at runtime, and BAM they're in the global factory. It works well with template instantiations too, otherwise you would have to list all template instantiations (and you wouldn't notice if you had extra).