Click here to Skip to main content
65,938 articles
CodeProject is changing. Read more.
Articles
(untagged)

Thread Safe Singleton Template and Management - No MFC Required!

0.00/5 (No votes)
7 Dec 2002 1  
Source code and explanation of a thread safe singleton and singleton management.

Sample Image - PDESingletonTemplateNoMFC.jpg

Contents

Introduction

Patterns are ready thought out solutions to common development problems. Correct application of patterns (and not just for their use sake!) create extendable and flexible applications.

In brief a Singleton is like an OO (Object Oriented) friendly global variable that can be shared amongst classes - accept with the bonus that wherever you use it, it is guaranteed to be the only instance of that class. Unlike static global variables whose creation time is random (well decided by the compiler via a mechanism you have no control over), you may also decide in which order singletons are created - essential if one relies on another existing first.

This particular pattern has had many implementations and interpretations - some interesting ones are available on this site, and I present mine here. So what makes this implementation of the singleton different?

  • It comes complete with an Observer class that cleans all singletons up.
  • It has been built with thread safety in mind.
  • It comes in three flavors - a simple example that can be changed and does not use templates, a singleton Container for any class, and a singleton Template from which the developer may inherit from, making a class a singleton in a couple of lines.
  • It doesn't use the MFC (but for those who cannot live without it (*coughs* CString), it can be used in MFC applications).
  • It probably could be ported to other platforms with minimum hassle.
  • It was done on my own time, it is mine, is well commented, was a great learning experience, and seems pretty darn good to me!

Comparing against other articles on the CodeProject, I would rate this one Beginner /Intermediate. The code itself may be not perfect and be able to be improved upon, but it should serve as a good illustration of a singleton, and conveys what I have tried to achieve.

Background

I've programmed for many years, and in June 2001 I graduated from university with a second class degree - B.Sc. (Hons) in Computer Studies. Patterns were part of the course, as was UML and other OO and standard programming techniques. Many people could not see the point of patterns, possibly because the projects set often hardly seemed to be worth the bother. Plus it was all part of the documentation thing, which most people don't find very fun!

Out in the real world (that began for me a week after I handed in my last piece of work!), the company I work for asked for these OO and UML skills, but were only sparsely using them in their own projects. Technical documentation was sparse or non-existent for many products, and even helpful in-code comments were thin on the ground. Being thrown in at the deep-end and asked to maintain one of their bigger products, while being given a sheet of A4 for something new they wanted me to work on, was pretty exciting.

Over a year on, and I have learned a lot from my colleagues and my own experiences. Not many people like writing technical design documentation, tests, or keeping product documentation up to date - but it really, REALLY would have been helpful to have that stuff at the start! All the stuff I do I comment liberally, keep a logbook and try to keep the technical docs up to date - in case I win the lottery and then some other poor soul has to take over what I have done!

After six months of writing a lot of comments in the existing products, and a straight-to-code prototype to prove technology for the new product, I decided to count my losses and redesign the new product I was working on, starting almost from scratch (apart from transferable functions). Why did I do this? Because the new product's specification had changed just enough so I would have to start hacking code around my own code to get things working right, and that was not how I wanted things to be.

I've successfully applied the singleton pattern to many different facets of many different projects - but each time I improved upon my interpretation of the pattern. But it came to a point where I was getting "funny" thread type errors in more complicated applications - those nasty ones that only occur one time in a million - but happen at the worst possible times. It also niggles at me a little each time I use copy and paste (an "Anti-Pattern"[5]) on a section of code - because it means I'm repeating myself (and possibly mistakes!) where I should not be.

In the last week of November 2002, I used a lot of my free time to investigate lightweight and MFC free thread synchronization technique[1], and investigated the singleton pattern further [2,3,4]. Each file started off empty (no copying and pasting!) - and has been built up using my own ideas and those from my references - and I feel is sufficiently different to publish as my own and share with you.

Using the code

Using the code - Quick version

To prevent class name clashes, I've put all these utility classes in my namespace (my initials PDE). For newbies, you get at functions and classes in a different namespace by using the scope resolution operator ::.

To use the template method just follow the example set in CSingletonExample.h. Inherit publically from PDE::TSingleton< CSingletonExample >, and optionally one of the thread synchronization classes.

#include "TSingleton.h"

#include "CLockableObject.h"


class CSingletonExample  : 
   public PDE::TSingleton< CSingletonExample >, public CLockableObject
{
    //You must make the template base a friend so

    //it can use the protected constructor

    friend class TSingleton< CSingletonExample >;

    protected:
        // Constructor must be private or protected so no 

        //other object can initiate its creation.

        
        //Must have appropriate default constructor!

        CSingletonExample(); 

    public:
        // The Destructor must be public so that 

        //other classes can trigger this object to properly

        //clean itself up


        ~CSingletonExample();

    public:
        // Class workings, blah blah blah...


/// and so on...

}

To save yourself some typing for the classes in the PDE namespace, in the cpp file you may want to have this below just after your includes...

using namespace PDE;

The constructor has to be protected or private. As I was forced to use the friend mechanism (thus far) it doesn't matter which. For proper cleanup to occur, you have to make your destructor public.

The example test stub that proves the Singleton produced via TSingleton is thread safe and is contained within SingletonTemplate.cpp. A simplified test stub is shown below.

#include "CSingletonExample.h"

#include "CSingletonObserver.h"


//.... etc etc ....


using namespace PDE;

//.... etc etc ....


int main(int argc, char* argv[])
{
    //.... Do something interesting


    // Get the pointer to the one and only instance. 

    // This value may be NULL if 

    //the unique instance has been killed.

    CSingletonExample* pCSingleton = 
           CSingletonExample::Instance();

    //Check we have a valid pointer...

    if (pCSingleton)
    {
        //Use it as you would anything else

        pCSingleton->SetTestInt(1);

        CSingletonExample::Instance()->DebugShow();
      }

    //The Observer must be called to delete 

    //the memory taken up by the Singleton

    CSingletonObserver::Instance()->RemoveAllAndDie();
}

The classes as supplied have used printf in some of their constructors and destructors to prove what they are doing, as you will see if you compile the project and run the exe. Notice how the CSingletonObserver class is asked for its instance at the end, then is told to clean up all the singletons and then itself.

That's it! The creation of the singletons themselves is thread safe, although of course any methods you make, you should make thread safe yourself - if the singleton is to be used in a multiple threaded application. The other classes in the project may give you a few ideas how to do this.

Using the code - Long version

This section will be added to if sufficient questions about other aspects of the code are asked.

Points of interest

The template for writing articles says: "Did you learn anything interesting/fun/annoying while writing the code? Did you do anything particularly clever or wild or zany?". Hmmm - a bit of all the above I guess!

Interesting & fun

Carrying the idea of the singleton one stage further into the realms of being unique - by creating a default behavior of one lifetime - once it's been created and destroyed - that's it - it can't be reborn. Makes sense if you think about it - having only one of something means that when it is gone, it's gone forever. This also has the effect of making sure that a singleton isn't recreated after the singleton observer has finished clearing them all up.

I also took this opportunity to create some basic thread synchronization helper classes, which although used here in this example - I am currently experimenting with and using now.

Annoying

I can understand the destructors of a singleton requiring to be public for proper clean up to take place, it sort of makes sense. Something did niggle at me a little more though. When creating a singleton from TSingleton, your class becomes part of the hierarchy if you inherit from it as I describe. Yet TSingleton cannot instantiate the singleton inside Instance without the inheriting singleton stating that TSingleton is a class friend. This I cannot quite grasp, as TSingleton is essentially part of the singleton itself, so it should have access to protected and private members anyway. Still I suppose doing what I did thrashes the language a little anyway, so perhaps I was lucky that C++ lets me go as far as I did!

Wild or zany

The template itself inheriting from a normal class was pretty cool. Despite looking for classes that inherited from templates, I did not manage to find any examples before writing this article and the related source, only a brief note saying that it could be done [6]. But of course while writing this all up, I did start to find examples [8] - Murphy's Law I guess!

Final note

If this article or source has helped you out, or even just fired up a few brain cells, drop me an e-mail. It's always nice hearing positive feedback! Of course if you think you have improved upon it, then I would be very interested in finding out how!

If I find time I'll expand on those thread synchronization classes with an example that I can publish here, if there is enough interest. Perhaps I'll maintain the classes on my own website too [7].

Hope this article helped you in some way, and good luck with whatever you are doing!

References

Please note that links to web resources may be inaccurate, as is the nature of the web.

  1. - Portable Thread Synchronization Using C++, Jim Frost (1995).
  2. - "Design Patterns - Elements of Reusable Object-Oriented Software", Erich Gamma et al (1995).
  3. - "Applying UML and Patterns", Craig Larman (1998).
  4. - "Singleton Creation the Thread-safe Way", Jonathan Ringle (1999).
  5. - "Anti-patterns", - <website with links to anti-pattern information> William Brown et al, (1998).
  6. - "C++ How To Program - Forth Edition", Deitel & Deitel (2003).
  7. - My homepage (Enjoysoftware) - www.enjoysoftware.co.uk, Paul Evans (2003).
  8. - "COM Singletons - A Dangerous Animal", Richard Blewett (2001).

Further reading

Thanks to Jonathan de Halleux, who provided a link to a set of classes including a different set of singleton templates, and some other patterns too. It's a port of the "Loxi" library, and is well worth looking at for some extra ideas.

History

  • Version 1 - 08-DEC-02
    • First revision submitted.
  • Version 1.1 - 09-Dec-02
    • Added "Further Reading" section

License

This article has no explicit license attached to it but may contain usage terms in the article text or the download files themselves. If in doubt please contact the author via the discussion board below.

A list of licenses authors might use can be found here