Reputation: 1703
I am working on a C++ project having multiple classes that must be singletons, with dependencies between them (order of initialization matters).
I have come up with this solution:
class MySingleton1 { protected: MySingleton1(); }
#include "MySingleton1.hpp" #include "MySingleton2.hpp" class Singletons : public MySingleton1, public MySingleton2 {} static Singletons s_singletons;
template<> MySingleton1& getSingleton() { return s_singletons; } template<> MySingleton2& getSingleton() { return s_singletons; }
template <class TSingleton> TSingleton& getSingleton();
Advantages:
Low-coupling:
Order of initialization can be controlled by changing the order of inheritance of the Singletons class
Disadvantages:
So, as far as I can see, this is actually fairly good so I'm thinking to leave it like this, but I'm asking for your feedback (what better place to do that than the programming community?).
What extra advantages/disadvantages do you see with this solution?
What alternatives do you suggest?
Upvotes: 5
Views: 1383
Reputation: 275565
This forces centralization of Singletons, which can mess up dependencies in more complex projects. The library that has singleton.cpp
must depend on everything needed for every singleton. At the same time, anyone who uses a singleton must depend on the singleton.cpp
library.
Basically your code could only work in a monolithic non-modular project. Scaling this to multiple dynamic libraries is next to impossible.
Your order of initialization must be maintained manually.
The static global variable's construction point is unsequenced with everything prior to the first expression in main
.
A decent solution I've used is to create a dynamic library that holds the singleton memory.
To be a singleton, you inherit from a CRTP helper, which provides a ::Instance()
inline method. People wanting the singleton use ::Instance()
.
::Instance()
creates a static local variable lifetime token. It then attempts to get storage for the singleton from the primary DLL; if the object is already created, it simply casts the storage into the object type, and increases its reference count.
If not, it creates new storage and constructs the object in it.
At the destruction of the static local variable lifetime token, it reduces the reference count. If that reference count hits 0, it destroys it locally in the current dynamic library.
The lifetime of the singleton is now the union of the lifetime of the ::Instance()
created variables. Destruction occurs in non-type-erased code, so we don't have to worry about the DLL with the code being unloaded. Storage is central. The DLL that stores the storage has to be lower level than every user of the Singleton system, but it in turn has no dependencies, so that isn't a pain.
This is far from perfect; singletons and lifetime are a constant problem, because clean program shutdown is hard and made harder by singleton's existence. But it has worked so far in a reasonably large project.
Upvotes: 6
Reputation: 57
Can you use dependency injection in your case? i.e. have some serialized code create an instance of each class and pass a reference to the instance(s) into the constructors of any other instances that need access to them? This might simplify your design, if applicable. Ideally you could pass a separate, newly-created instance into each constructor to further reduce coupling but it seems you need to share state. In any case, perhaps it helps.
Upvotes: 0