Feedback Search Top Backward Forward
EDM/2

Singletons - The Next Generation

Written by Stefan Ruck

Linkbar

 

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 Result

As 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 new constructor. Secondly, once a static singleton is created, it can't be destroyed. My implementation destroys the singleton as soon as the reference counter reaches zero, in other words, once the singleton is no longer needed. This is a very important fact to remember for later on.

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 Class

Writing 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:

template <class T>
class ASingletonImpl : public T
{
  public:
    // number of users
    static unsigned long referenceCounter ()
      { return (ASingletonImpl <T> :: m_ulReferenceCounter); }

  protected:
    ASingletonImpl () { }

 private:
    // not implemented
    ASingletonImpl (const ASingletonImpl <T> & rSource);

    // not implemented
    ASingletonImpl <T> & operator = (const ASingletonImpl <T> & rSource);

    // return the instance
    static ASingletonImpl <T> * instance ();

    // release the instance
    static void releaseInstance ();

    // pointer to the single instance
    static ASingletonImpl <T> * m_pInstance;

    // counts the instance requests
    static unsigned long m_ulReferenceCounter;

    friend class ASingletonST <T>;
};

Figure 2: The Singleton Implementation Class

The implementation of the class looks like this:

// initialize the pointer
template <class T>
ASingletonImpl <T> * ASingletonImpl <T> :: m_pInstance = 0L;

// initialize the counter
template <class T>
unsigned long ASingletonImpl <T> :: m_ulReferenceCounter = 0L;

//****************************************************************
// ASingletonImpl :: instance - returns a pointer to the instance*
//****************************************************************

template <class T>
ASingletonImpl <T> * ASingletonImpl <T> :: instance ()

{
  // do we have an instance already?
  if (!m_pInstance)
      // no, allocate it
      m_pInstance = new ASingletonImpl <T>;

  // increase the counter
  ++m_ulReferenceCounter;

  // return the pointer to the instance
  return (m_pInstance);
}

//***********************************************************
// ASingletonImpl :: releaseInstance - releases the instance*
//***********************************************************

template <class T>
void ASingletonImpl <T> :: releaseInstance ()

{
  if (!m_ulReferenceCounter)  // no instance allocated?
      return;                 // yes, return

  // decrease the number of active users
  --m_ulReferenceCounter;

  // is the instance still in use?
  if (m_ulReferenceCounter)
      return;                 // yes, return

  delete m_pInstance;        // delete the instance

  m_pInstance = 0L;          // reset the pointer
}

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 #include statements in any file. Just put all the includes you need into the project's configuration file and the compiler will do the job.

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 Generation

Thinking 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.

template <class T>
class ASingletonST

{
  public:
    ASingletonST ()          // constructor
      { instance (); }
    virtual ~ASingletonST () // destructor
      { releaseInstance (); }

    T * operator -> ();

    T &  operator * ();

  private:
    // not implemented
    ASingletonST (const ASingletonST <T> & rSource);
    // not implemented
    ASingletonST <T> & operator = (const ASingletonST <T> & rSource);

    // create the instance and/or increment refcount
    static void instance ();

    // release the instance
    static void releaseInstance ();

    // pointer to the one and only instance
    static T * m_pInstance;

    // reference counter
    static unsigned long m_ulReferenceCounter;
};

Figure 4: ASingletonST's New Interface

The new implementation of ASingletonST looks like this.

// define and initialize the pointer
template <class T>
T * ASingletonST <T> :: m_pInstance = 0L;

// define and initialize the counter
template <class T>
unsigned long ASingletonST <T> :: m_ulReferenceCounter = 0L;

//*****************************************************************
// ASingletonST :: operator -> - returns a pointer to the instance*
//*****************************************************************

template <class T>
T * ASingletonST <T> :: operator -> ()
{
  if (!m_pInstance)
      instance ();

  return (m_pInstance);
}

//******************************************************************
// ASingletonST :: operator * - returns a reference to the instance*
//******************************************************************

template <class T>
T & ASingletonST <T> :: operator * ()
{
  if (!m_pInstance)
      instance ();

  return (*m_pInstance);
}
//********************************************************
// ASingletonST :: instance - create the instance and/or *
//                            increment refcount         *
//********************************************************

template <class T>
void ASingletonST <T> :: instance ()
{
  // do we have an instance already?
  if (!m_pInstance)
      // no, allocate it
      m_pInstance = new T;

  // increase the counter
  ++m_ulReferenceCounter;
}

//***********************************************************
// ASingletonST :: releaseInstance - releases the instance  *
//***********************************************************

template <class T>
void ASingletonST <T> :: releaseInstance ()
{
  if (!m_ulReferenceCounter)  // no instance allocated?
      return;                 // yes, return

  // decrease the number of active users
  --m_ulReferenceCounter;

  // is the instance still in use?
  if (m_ulReferenceCounter)
      return;                // yes, return

  delete m_pInstance;        // delete the instance

  m_pInstance = 0L;          // reset the pointer
}

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 Pattern

Ittay 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 Attempt

To fulfil the tasks described above, you might create a singleton manager which looks like this:

class ASingletonManager
{
public:
  virtual ~ASingletonManager();
//whatever
private:
  ASingletonST<class1> m_Class1;
  ASingletonST<class2> m_Class2;
  ASingletonST<class3> m_Class3;
}

Figure 6: A Singleton Manager

And to alter the singleton destruction sequence from the default, you could code the destructor like this:

ASingletonManager::~ASingletonManager()
{
  m_Class1.ASingletonST<class1>::~ASingletonST();

  m_Class3.ASingletonST<class3>::~ASingletonST();

  m_Class2.ASingletonST<class2>::~ASingletonST();
}

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.

class ASingletonManager
{
public:
  ASingletonManager();
  virtual ~ASingletonManager();
//whatever
private:
  ASingletonST<class1> * m_pClass1;
  ASingletonST<class2> * m_pClass2;
  ASingletonST<class3> * m_pClass3;
}

ASingletonManager :: ASingletonManager()
                     : m_pClass1 (0L),
                       m_pClass2 (0L),
                       m_pCLass3 (0L)
{
  m_pClass2 = new ASingletonST<class2>;

  m_pClass3 = new ASingletonST<class3>;

  m_pClass1 = new ASingletonST<class1>;
}

ASingletonManager :: ~ASingletonManager()
{
  delete m_pClass3;

  delete m_pClass1;

  delete m_pClass2;
}

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) Way

Implementing 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.

class A
{
public:
        ~A() { cout << "A::~A" << endl; }

        void text() { cout << "A::text" << endl; }

private:
        A () { cout << "A::A" << endl; }
        A (const A & rSrc); // not implemented

        // not implemented
        const A & operator= (const A & rSrc);

        friend class ASingletonManager;
};

class BDependsOnA
{
public:
        ~BDependsOnA() { cout << "BDependsOnA::~BDependsOnA" << endl; }

        void text() { cout << "BDependsOnA::text" << endl; }

private:
        BDependsOnA () { cout << "BDependsOnA::BDependsOnA" << endl; }
        BDependsOnA (const BDependsOnA & rSrc); // not implemented

        // not implemented
        const BDependsOnA & operator= (const BDependsOnA & rSrc);

        friend class ASingletonManager;
};

class ASingletonManager
{
public:
        virtual ~ASingletonManager()
    { cout << "ASingletonManager::~ASingletonManager" << endl; }

        A & a() { return (m_A); }
        BDependsOnA & bDependsOnA() { return (m_BDependsOnA); }

private:
        ASingletonManager()
    { cout << "ASingletonManager::ASingletonManager" << endl; }

        // not implemented
        ASingletonManager(const ASingletonManager & rSrc);

        // not implemented
        const ASingletonManager &
        operator=(const ASingletonManager & rSrc);

   A m_A;
   BDependsOnA m_BDependsOnA;

   friend class ASingletonST<ASingletonManager>;
};

void f2()
{
        cout << "f2" << endl;

        ASingletonST pSingletonManager;

        pSingletonManager->bDependsOnA().text();

        pSingletonManager->a().text();
}

void f1()
{
        cout << "f1" << endl;

        ASingletonST pSingletonManager;

        pSingletonManager->a().text();

        f2();

        pSingletonManager->bDependsOnA().text();
}

void main()
{
        f1();
}

Figure 9: Example of Using the Singleton Manager

The output of the sample code is this:

f1
A::A
BDependsOnA::BDependsOnA
ASingletonManager::ASingletonManager
A::text
f2
BDependsOnA::text
A::text
BDependsOnA::text
ASingletonManager::~ASingletonManager
BDependsOnA::~BDependsOnA
A::~A

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.

Conclusion

I 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!

Acknowledgements

I'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
[2] Bjarne Stroustrup The C++ Programming Language, Second Edition, 1995, p. 576f
[3] Bjarne Stroustrup The C++ Programming Language, Second Edition, 1995, p. 514
[4] Bjarne Stroustrup The C++ Programming Language, Second Edition, 1995, p. 580
[5] Bjarne Stroustrup The C++ Programming Language, Second Edition, 1995, p. 648

 

Linkbar