Singletons - The Next GenerationWritten by Stefan Ruck |
In December 1997 I introduced my version of the Singleton template class in EDM/2 [1]. Now, some time has gone by and Anapum Kapoor and Ittay Freiman inspired me to rethink the design. If you haven't done so yet, please read my original article first to be able to understand all the points I discuss here. Another Approach - (Nearly) The Same ResultAs promised in the first Singleton article, I will first show you another way to attain nearly the same result. Why I say 'nearly' will be explained shortly. Björn Fahller, another EDM/2 author, was the first who showed me a different way to achieve the goals of a singleton. And since I feel he provided a really good explanation, I will just quote his mail. From now on, I will refer to this implementation as the 'static singleton'. Here is what Björn wrote: The way out is totally different:
class X // our singleton class { // whatever. // Make sure assignment and all constructors are private. public: static X& instance(void); }; X& X::instance(void) { static X x; return x; } Figure 1: Static Singleton Whenever you need your singleton in your code, call X::instance().someMethod(); Sure, different syntax, but the meaning is the same, and it's safe from the semantic drawbacks (it'll even be destroyed.) It has one potential real drawback, though. The object is constructed the first time used, which makes it very improper for real-time systems (you never know which call will create it, and thus cannot know how long a call will take).
That's what Björn wrote. Now you might ask what's the difference besides
having less code (no reference counting is needed and no template class).
There are two. First, the static singleton is located in the program's static
storage, while mine is put on the heap since it is created by calling
the But if you do not need or want to destroy and recreate a singleton at runtime, I think the static singleton is the way to go. The (Temporary) Singleton Implementation ClassWriting sample code for this article, I first decided to create a little helper to avoid having to recode a few methods for each singleton class. Looking at the sample code in [1], you might notice it's necessary to implement the static data members m_ulReferenceCounter, m_pInstance and the static methods instance() and releaseInstance() for each class that should be used as a singleton. Of course, copy and paste goes very fast, but there are ways in C++ to avoid this. First, you might ask why not creating a base class which implements these four members. This is not desirable for two reasons. First, you might need multiple inheritance which is not allowed in all organizations (for whatever reasons). Secondly, the base class can only handle a pointer of its own type, not of the final singleton class, since we can't know each derived class will ever be created when defining it. So this is not the solution. What can we do? Reading a little bit about Microsoft's ATL gave me the final idea. A client of a singleton based on my ASingletonST class can only call its public members. Also, a default constructor is necessary to clearly initialize the object without having to use any sort of initialization method. So why not have a class that uses the singleton class as its base class? implements the singleton specific members and is the one used by ASingletonST. This can easily be done by using a template class. Here is the declaration:
Figure 2: The Singleton Implementation Class
The implementation of the class looks like this:
Figure 3: Implementation of the Singleton Implementation Class Comparing this to the implementation of the sample code in [1], you will notice two differences. The somewhat cryptic declaration of the static data members and the allocation of an object of type ASingletonImpl <T> instead of the singleton class itself in the instance() method. The rest has been left unchanged. The Drawbacks
I used IBM's VisualAge 4.0 compiler to implement the sample code
with ASingletonImpl and a modified version of ASingletonST. This compiler
has one big advantage against any other compiler I know: you don't
need to worry about Trying to use these classes at work with Microsoft's VC++ 6.0 I ran into unresolvable problems because of cross-references between ASingletonST and ASingletonImpl. The result was I couldn't use the classes. The Next ASingletonST GenerationThinking about it a bit, I asked myself why not combine the advantages of ASingletonImpl with ASingletonST. No sooner thought than done. ASingletonST got a new interface and implementation.
Figure 4: ASingletonST's New Interface
The new implementation of ASingletonST looks like this.
Figure 5: Implementation of the new ASingletonST Class To avoid any complications, I've made the copy constructor and assignment operator private and didn't implemented them. Since I know now that an explicit call of the destructor is possible [2], I've expanded the members for security reasons. Using this version, it is safe (but not desirable) e.g. to call ASingletonST's destructor and afterwards the -> operator. Implementing a 'real' singleton class using ASingletonST requires only two things: be sure to declare operator= and all constructors as private and make ASingletonST<class> a friend of it. But ASingletonST also gives you the ability to use any class as a singleton. One thing about classes not designed as singletons is that you can't prevent them from not being used by others as a singleton. The Singleton Manager Design PatternIttay Freiman was the first who introduced the idea of a 'singleton manager' since he had the problem of using singletons depending on each other and so needed something that controls the sequence of their construction and destruction. Using a static singleton makes it impossible to exercise control over the sequence since the sequence in which local static objects are destroyed is not defined (see C++ Reference Manual [3]). So my solution of using pointers and reference counting is needed. The singleton manager's task is to take control over the sequence of creation and destruction of singletons that depend on each other. Therefore, it needs to be created before any client might need any singleton and should be destroyed only when no one needs any more singletons. One thing really helpful here is the definition of when the constructors and destructors of data members are called. The Standard says "First, the base classes are initialized in declaration order ..., then the members are initialized in declaration order (independent of the order of mem-initializers)... The declaration order is used to ensure that ... members are destroyed in the reverse order of initialization." [4]. With this in mind, it's quite easy to create a singleton manager that ensures the right sequence of singleton creation and destruction. Just declare a data member for each singleton needed in the right sequence: independent singletons first, dependent ones later. If, for whatever reason, you need a different order of destruction, you might call the destructor of the data members inside the manager's destructor in the sequence you need. This is why I check for a valid instance in ASingletonST::operator-> and ASingletonST::operator*. You can (but I think should not) call the destructor of a class whenver you like. A First AttemptTo fulfil the tasks described above, you might create a singleton manager which looks like this:
Figure 6: A Singleton Manager And to alter the singleton destruction sequence from the default, you could code the destructor like this:
Figure 7: Example of Controlling the Sequence of Destruction In this sample, m_Class1 will be initialized first, then m_Class2 and lastly m_Class3. Assuming there is no other client currently using one of the singletons, in ASingletonManager::~ASingletonManager() the class1 singleton will be destroyed first, then class3 and finally class2. The order without an explicit call to ASingletonST<>::~ASingletonST() would be class3 first, class2 second and class1 third. But this example conflicts with the ANSI/ISO standard which says "A destructor may not be invoked more than once for an object"[5]. Here, it will be called twice: first explicit by the manager's destructor, and later implicitly when ASingletonManager's data members are destroyed. To be completely free of any declaration order, you need to implement the ASingletonST members of the manager as pointers, which will be allocated by the constructor, independent of the order in which the pointer members are declared.
Figure 8: Using Pointers to ASingletonST Any time the program needs an instance of the singleton, an object of ASingletonST<class> can be created and used. The instance returned by this object is the one created by the singleton manager. But remember to make sure a manager is created before any user creates an ASingletonST<> object that refers to a singleton handled by the singleton manager. And the manager must stay alive until no more managed singletons are needed. Another (Better) WayImplementing a singleton manager as described above, you have to take care that the singleton manager is the first to reference the singleton and the last to free the reference. To eliminate this disadvantage, some changes are needed. We have to make sure that no client can create a singleton without using the singleton manager. So first declare the constructors of any class which should have only one instance as private and make the singleton manager a friend class. Second, implement the manager itself as a singleton. Third, implement a public interface to access the 'one-instance' members. The 'one-instance' classes don't need to be designed as singletons any more since no one but the singleton manager can create an object of that class. Only the manager must be a singleton. Now any client that needs access to the 'one-instance' objects first needs to create an object of ASingletonST<SingletonManager> which guarantees the right construction and destruction order of the 'one-instance' objects. The 'one-instance' objects can only be accessed through the singleton manager's interface. The sample code below shows how to do it, using the new ASingletonST class.
Figure 9: Example of Using the Singleton Manager
The output of the sample code is this:
Figure 10: Sample Code Output You might wonder why A::A and BDependsOnA::BDependsOnA is printed out before ASingletonManager::ASingletonManager. The answer is that the constructors of A and BDependsOnA are called during member initialization which occurs before the ASingletonManager::ASingletonManager code is executed. ConclusionI think it's much easier now to create singletons using the new ASingletonST template class. It saves you from having to code singleton implementation details over and over again, lets you put the focus on the singleton classes and allows you to use classes never meant to be singletons as singletons. The Singleton Manager Design Pattern is one thing I really like. I've been using it since the days I first designed it in a commercial real-world program and it works fine. I hope you enjoy it too! AcknowledgementsI'd like to thank Anapum Kapoor and Ittay Freiman for initiating my thinking about the ASingletonST design again. Thanks to Ittay also for his basic idea about the singleton manager. Thanks again to the EDM/2 guys, I hope you can manage to keep EDM/2 alive (support them by writing lots of articles!).
[1] Stefan Ruck,
Let's Talk About... Singleton, EDM/2, Volume 5 Issue 12 |