I did something hugely unorthodox with singletons and lots of C++ magic today that I never thought would work, but in the end, actually managed to figure out!
So, I thought I'd share my story!
First, the problem.
I had a setup where I was building a menu and wanted to insert menu entries into it:
Each entry in the menu was a separate class, they were all stored in a single STL vector, and I was constructing it like this:
std::vector<BaseClass*> entries;
...
constructor(){
entries.push_back(new A());
entries.push_back(new B());
...
}
This was a total pain, because I wanted all objects that were inheriting a single base class added into this menu!
...
entries.push_back(new Y());
...
entries.push_back(new XC());
...
Not to mention I was expecting that I would soon want to move the entries out into a setting where I could just add entries to a list after compiling in some kind of configuration file:
entries = {A,B,C,D,E,Q,R7,ZZ}
So, I thought, what better way of managing this than a factory which could construct the objects for me?
Factory::getSingleton()->create("A");
That way, I could just add them to a single list somewhere, and be done with it!
But, this would still be a pain. Somewhere, in my code, I would still need a repetetive central structure:
Factory::getSingleton()->addEntry(new Constructor<A>(), "A");
Factory::getSingleton()->addEntry(new Constructor<B>(), "B");
...
That anyone who made a new class would have to find and add entries to!
No way!
My line of thought was that I would rather add a few more lines into each class and have them added automatically to the factory with the adding isolated to the class, rather than a central structure that everyone would have to modify.
class A : public Base {
[someConstruct]
};
Rather than
void addFactoryObjects(){
[giganormousList]
}
So, how on earth would I do that?
Well, first thing's first. I needed a factory, and I needed to be able to access it from anywhere.
class Factory{
std::map<std::string, Constructor*> constructors;
static Factory _instance;
public:
static Factory* getSingleton(){ return &_instance; }
void addConstructor(Constructor *c, std::string s){ constructors[s] = c; }
Base* construct(){ return constructors[s]->construct(); }
};
So, I built a singleton.
Singleton, thought I. They're constructed... without having to write any external code!
Gasp!
So, I thought I could add something into each class, that would add to my factory when I constructed it:
class Constructor{
virtual Base* construct() = 0;
};
template<class T>
class ConstructorTemplate{
Base* construct(){return new T();}
};
template<class T>
class ConstructorCreator{
ConstructorCreator(){
Factory::getSingleton()->addConstructor(new ConstructorTemplate<A>(), "name");
}
};
class A : public A{
static ConstructorCreator<A> creator;
};
Easy as pie! But, it leaves some things to be desired!
First, I needed to find the right name, and as per template rules, I couldn't do it in the template! (template <const char * > is not allowed!)
Which left me with the option of adding a function or a constant, I chose a function:
class A : public Base{
static ConstructorCreator<A> creator;
public:
static std::string getFactoryName(){return "name";}
}
Which was, of course, a bit more code to write, but I could still isolate everything to the class, my goal in the first place!
So, I would have hoped I was finished here, but I ran into a problem!
My code, it crashed!
Seemingly, my simplistic implementation of the singleton pattern worked against me!
Or, more accurately, constructing anything beyond a simple structure isn't done at compile-time, but run-time! This meant that the factory I hoped would always be there wasn't constructed when I tried to access it! Horror!
Fixing it was easier than you'd think, by going back to the original Singleton pattern:
class Factory{
static Factory *_inst;
public:
static Factory* getSingleton(){
if(_inst == 0) _inst = new Factory(); //Multi-threading, what do you mean?
return _inst;
}
};
Factory* Factory::_inst = 0;
Magic! It worked!
But! I introduced a new problem! My factory wasn't destroyed when I quit! Think of the damage my uncleared memory could cause on soon to be completely invalid memory!
Solving this in the easy way was no fun:
main(){
delete Factory::_inst;
return 0;
}
And could prove problematic if I used other constructs, such as exit(int)!
How could I destroy the object when there was nothing that could do it for me? Or was there?
Oh yes. Static members.
Static members are handled very nicely by C++, since they are always destroyed when a program exits, meaning I could use a static destructor to destroy my static pointer!
struct Destructor{
~Destructor(){ if(Factory::_inst) delete Factory::_inst; }
};
All I had to do, was at some point create it:
class Factory{
...
static Destructor destroyer_of_worlds;
};
With this, I could efficiently destroy any singleton pointer before exit, which was perfect for my needs!
(Also, I found it funny how all my factory management is done before and after the main loop.)
With all of this finished, I could build my program:
int main(int, char**){
Factory::construct("Pie")->eat();
return 0;
}
This was my foray into static objects, I had fun, I hope you found it interesting.
![Gentleman](https://forums.tigsource.com/Smileys/derek/gentleman.gif)
Also, I'm curious as to how people react to a construct like this. I'm isolating all necessary data to move an object from being constructed via an explicit constructor to being constructed implicitly by a factory, meaning I'm containing what is necessary for a specific event to occur in a place related to the class causing it, so I'm adhering to some sort of object orientation principle.
At the same time, I'm using a couple of constructs I've never seen before and storing a few static instances during run-time for no real reason.
Is this bad code, horrible obfuscation, good object oriented isolation of behavior, totally insane or, as I see it, just slightly, slightly awesome?
![Cool](https://forums.tigsource.com/Smileys/derek/cool.gif)
Linus