Home arrow C++ arrow Page 4 - C++ In Theory: The Singleton Pattern, Part 2
C++

C++ In Theory: The Singleton Pattern, Part 2


In our second article on the Singleton pattern, J. Nakamura discusses a reusable code version of the pattern.

Author Info:
By: J. Nakamura
Rating: 5 stars5 stars5 stars5 stars5 stars / 41
January 25, 2005
TABLE OF CONTENTS:
  1. · C++ In Theory: The Singleton Pattern, Part 2
  2. · Singleton Generalization
  3. · Testing Our Generic Implementation
  4. · Singletons are not for Simpletons

print this article
SEARCH DEVARTICLES

C++ In Theory: The Singleton Pattern, Part 2 - Singletons are not for Simpletons
(Page 4 of 4 )

While the implementation looks deceptively simple and stable, it harbors a pretty nasty problem: the sequence of static object destruction is not given when an application is being terminated. Imagine two singletons A and B using a logging class L (which is the singleton from the previous article), and after A has used the log in a regular way, the other singleton B causes the application to terminate.

The construction sequence might very well be A – L – C when C fails and terminates the application. So C is destructed first, followed by the Log singleton L, and finally singleton A is destroyed. There is no reason why A won’t attempt to write to the Log while it is being destroyed (destructors are a good location to dump the internal state of an object to the log). But the Log instance was just destroyed! So LOG::Instance().Write() will at best yield an exception or at worst cause your application to terminate the hard way!

The book Modern C++ Design [Alexandrescu] describes this problem in detail and demonstrates multiple solutions, from using the Phoenix Singleton to revive dead singletons, to using Policy template classes to control a Singleton's lifetime. The book is very advanced, very enjoyable and very useful. I have chosen to demonstrate an easy way out to you for now. We will implement a policy option to create either a Meyers Singleton or a Gamma Singleton and leave it at that for now.

If you have read the previous article, you will know that the Meyers Singleton is an implementation that prevents memory leakage. The Gamma Singleton, on the other hand, doesn’t clean up after itself and this comes in handy with our destruction sequence problem. The best way to guarantee our singleton will remain alive to the very end is by simply not destroying it!

Please note that we will depend on the operating system to clean up memory for us, and we have to be very careful not to create a resource leak! Take a look at the Loki library [Alexandrescu] to find out what the complete solution is when the ones I have given you won’t suffice anymore (or when you are curious of course). It will be very trivial to replace these singletons with the Loki ones.

Our singleton needs two different methods of creation. If we externalize these creation methods from the singleton class and provide one of them as a template parameter, the singleton class has become configurable as far as its creation is concerned. This is what policies [Alexandrescu] are all about.

I have constructed a CreateGamma and a CreateMeyers class to demonstrate this concept:

// singleton.h
#ifndef __SINGLETON_H
#define __SINGLETON_H

//
// for a definite implementation of the singleton check out
//
http://sourceforge.net/projects/loki-lib/
//
// or the book “Modern C++ Design” by A.Alexandrescu
//

// This is how a Gamma Singleton would instantiate its object.
template <class T> struct CreateGamma {
  static T* Create() { return new T; }
};

// This is how a Meyers Singleton would instantiate its object.
template <class T> struct CreateMeyers {
  static T* Create() {
    static T _instance;
    return &_instance;
  }
};

// This Singleton class accepts different creation policies.
template <class T, template<class> class CreationPolicy=CreateMeyers>
class Singleton {
public:
  static T& Instance() {
    if (!m_pInstance)
      m_pInstance=CreationPolicy<T>::Create();
    return *m_pInstance;
}

private:
  Singleton();          // ctor hidden
  ~Singleton();          // dtor hidden
  Singleton(Singleton const&);    // copy ctor hidden
  Singleton& operator=(Singleton const&);  // assign op hidden

  static T* m_pInstance;
};

template <class T, template<class> class C>
T* Singleton<T,C>::m_pInstance=0;

#endif // __SINGLETON_H
// eof

Now we can add the following code to our test in main.cpp:

typedef Singleton<MyClass, CreateGamma> MyClassLeaking;

void test3()
{
int val=MyClassLeaking::Instance().bar();
MyClassLeaking::Instance().foo(++val);
(void)MyClassLeaking::Instance().bar();
}

// in main()
  (void)printf(“\n*** using leaking singleton ***\n”);
  MyLeakingSingleton::Instance().foo(0x0100);
  (void)printf(“*** test3 ***\n”);
  test3();
  (void)printf(“*** test3 ***\n”);
  test3();

And see that it yields the desired leakage:

*** using leaking singleton ***
MyClass #4 constructed.      #here our singleton is constructed
MyClass4::foo(0x00000100) called.
*** test3 ***
MyClass4::bar() called.
... m_Value is: 0x00000100.
MyClass4::foo(0x00000101) called.
MyClass4::bar() called.
... m_Value is: 0x00000101.
*** test3 ***          #it lives fine across all calls
MyClass4::bar() called.
... m_Value is: 0x00000101.
MyClass4::foo(0x00000102) called.
MyClass4::bar() called.
... m_Value is: 0x00000102.

--- DONE ---
MyClass #3 destructed.      #the Meyers Singleton is destroyed
            #and our Gamma Singleton is still alive!

At first I thought it would be easy to describe the singleton, until I remembered the troubles I’ve had with construction and destruction sequences of multiple interdependent singletons. Hopefully I have been able to shed some insight into the usage of this "simple" design pattern.

References

[Gamma] Design Patterns: E.Gamma, R.Helm, R.Johnson and J.Vlissides.
[Alexandrescu] Modern C++ Design: A.Alexandrescu.
[Meyers] More Effective C++: S.Meyers.


DISCLAIMER: The content provided in this article is not warranted or guaranteed by Developer Shed, Inc. The content provided is intended for entertainment and/or educational purposes in order to introduce to the reader key ideas, concepts, and/or product reviews. As such it is incumbent upon the reader to employ real-world tactics for security and implementation of best practices. We are not liable for any negative consequences that may result from implementing any information covered in our articles or tutorials. If this is a hardware review, it is not recommended to open and/or modify your hardware.

blog comments powered by Disqus
C++ ARTICLES

- Intel Threading Building Blocks
- Threading Building Blocks with C++
- Video Memory Programming in Text Mode
- More Tricks to Gain Speed in Programming Con...
- Easy and Efficient Programming for Contests
- Preparing For Programming Contests
- Programming Contests: Why Bother?
- Polymorphism in C++
- Overview of Virtual Functions
- Inheritance in C++
- Extending the Basic Streams in C++
- Using Stringstreams in C++
- Custom Stream Manipulation in C++
- General Stream Manipulation in C++
- Serialize Your Class into Streams in C++

Watch our Tech Videos 
Dev Articles Forums 
 RSS  Articles
 RSS  Forums
 RSS  All Feeds
Write For Us 
Weekly Newsletter
 
Developer Updates  
Free Website Content 
Contact Us 
Site Map 
Privacy Policy 
Support 

Developer Shed Affiliates

 




© 2003-2017 by Developer Shed. All rights reserved. DS Cluster - Follow our Sitemap
Popular Web Development Topics
All Web Development Tutorials