Go4Expert

Go4Expert (http://www.go4expert.com/)
-   C++ (http://www.go4expert.com/articles/cpp-tutorials/)
-   -   Search for Robust Singleton Design Pattern (http://www.go4expert.com/articles/search-robust-singleton-design-pattern-t15868/)

asadullah.ansari 19Jan2009 14:28

Search for Robust Singleton Design Pattern
 

Background



Normally most of software Engineer uses singleton design pattern in daily coding life. It's very simple , vast uses but still i saw in many softwares which is not handled proper in C++ based products/projects. One thing more you can'nt design singleton as perfectly behaved singleton. But here approch and solution for how to make perfact singleton class in C++.

Why Singleton: To maintain object as single instance which is used by more than one user.

Details & Step by Step Improvement



Step 1: In first sight, singleton can be create as
Code:

class Singleton
{
      public:
          static Singleton * CreateSingleton();
 
      private:
        static Singleton *instSingleton_mp;
        Singleton(){ ; }
};

//Definition
Singleton* Singleton::InstSingleton_mp = NULL;
Singleton* Singleton::CreateSingleton()
{
      if (NULL = = InstSingleton_mp)
      {
          InstSingleton_mp = new Singleton();
      }
      return InstSingleton_mp;
}

Now Can you say Is it complete design of Singleton..I hope more than 99% people will say NO !!! We have to take care about copy constructor .We should put copy constructor in private section also because any one can create a new object based on already created singleton object. Okay lets go ahead with step 2.

Step 2: Adding Copy Constructor in private section

Code:

class Singleton
{
      public:
          static Singleton * CreateSingleton();
       
      private:
        static Singleton *instSingleton_mp;
        Singleton(){ ; }
        Singleton(const Singleton &) { ; }
};

//Definition
Singleton* Singleton::InstSingleton_mp = NULL;
Singleton* Singleton::CreateSingleton()
{
      if (NULL = = InstSingleton_mp)
      {
          InstSingleton_mp = new Singleton();
      }
      return InstSingleton_mp;
}


Now Again Some people (Now i hope 95%) will say it's not complete design, we should take of memory management. Okay Now we will go for that.

Step 3: Adding memory management code

Code:

class Singleton
{
      public:
          static Singleton * CreateSingleton();
          ~Singleton();
       
      private:
        static Singleton *instSingleton_mp;
        Singleton(){ ; }
        Singleton(const Singleton &) { ; }
};

//Definition
Singleton* Singleton :: InstSingleton_mp = NULL;
Singleton* Singleton :: CreateSingleton()
{
      if (NULL = = InstSingleton_mp)
      {
          InstSingleton_mp = new Singleton();
      }
      return InstSingleton_mp;
}
Singleton :: ~ Singleton()
{
    detele  InstSingleton_mp;
}

Again problem will be now infinite time destructor will be called by line delete operator.
Okay It can be done by little bit changes.

Step 4: Adding memory management code ( Continued )

Code:

class Singleton
{
      public:
          static Singleton * CreateSingleton();
          ~Singleton();
          void DestroyMe();
      private:
        static Singleton *instSingleton_mp;
        Singleton(){ ; }
        Singleton(const Singleton &) { ; }
};

//Definition
Singleton* Singleton :: InstSingleton_mp = NULL;
Singleton* Singleton :: CreateSingleton()
{
      if (NULL = = InstSingleton_mp)
      {
          InstSingleton_mp = new Singleton();
      }
      return InstSingleton_mp;
}
Singleton :: DestroyMe()
{
      if( NULL ! =  InstSingleton_mp )
    {
            detele  InstSingleton_mp;
            InstSingleton_mp=NULL;
    }
}


Now I hope 90% people will say again it has problem, suppose lot of users using this application then ... Suppose X guy uses this object and release it and other guy Y still wants to use this object then problem.....Okay lets see in next step.

Step 5: Introducing reference counts (In our software a class is available for it)

Code:

class Singleton
{
      public:
          static Singleton * CreateSingleton();
          ~Singleton();
          void DestroyMe();
      private:
        static Singleton *instSingleton_mp;
        static int refCount;
        Singleton(){ ; }
        Singleton(const Singleton &) { ; }
};

//Definition
Singleton* Singleton :: InstSingleton_mp = NULL;
int Singleton :: refCount = 0;
Singleton* Singleton :: CreateSingleton()
{
      if (NULL = = InstSingleton_mp)
      {
          InstSingleton_mp = new Singleton();
          ++refCount;
      }
      return InstSingleton_mp;
}
Singleton :: DestroyMe()
{
      if( NULL ! =  InstSingleton_mp && 0 = = refCount)
    {
            detele  InstSingleton_mp;
            --refCount;
            InstSingleton_mp=NULL;
    }
}


Now hope people will say it's perfect solution. But still i hope 80% people will say it has problem ...Suppose One user calling DestroyMe() function more than one times, lets say five times then no probs for a while( on that time refCount will be -4 ) but problem will be raised when next user is calling createSingleton() (refCount will be -3) and calking DestroyMe() in which destructor itself will not be called. A liitle bit changes can be done.

Code:

Singleton :: DestroyMe()
{
      if( NULL ! =  InstSingleton_mp && 0 = = refCount)
    {
            detele  InstSingleton_mp;
            if(0 < refCount)
              --refCount;
            InstSingleton_mp=NULL;
    }
}

Now To make it complete singleton class some more addition is required. Like assignment operator is required or not...Offcourse it's required but it should be return same object.

One more have putted destructor in public better if we will put in private because more constructor which is not in private can be putted in public section. To avoid this situation better if you put destructor in private section.

Coplete design of singleton is as:

Code:

class Singleton
{
      public:
          static Singleton * CreateSingleton();
          void DestroyMe();
          Singleton& operator= (const Singleton& );
      private:
        static Singleton *instSingleton_mp;
        static int refCount;
        Singleton(){ ; }
        Singleton(const Singleton &) { ; }
        ~Singleton();
};

//Definition
Singleton* Singleton :: InstSingleton_mp = NULL;
int Singleton :: refCount = 0;
Singleton* Singleton :: CreateSingleton()
{
      if (NULL = = InstSingleton_mp)
      {
          InstSingleton_mp = new Singleton();
          ++refCount;
      }
      return InstSingleton_mp;
}

Singleton :: DestroyMe()
{
      if( NULL ! =  InstSingleton_mp && 0 = = refCount)
    {
            detele  InstSingleton_mp;
            if(0 < refCount)
              --refCount;
            InstSingleton_mp=NULL;
    }
}
Singleton& operator= (const Singleton& obj)
{
  return *this; 
}

References



1. GoF http://www.vincehuston.org/dp/
2. http://hitechi.19.forumer.com/viewtopic.php?t=41
3. Idea for Style of writing this article is taken from code guru.

Note: Code written in this article is morally as designed purpose so dont expect it's runnable code. You may get compilation error.

madlex 23Jan2009 05:16

Re: Search for Robust Singleton Design Pattern
 
Sorry for telling you that, but your "robust" singleton design is closer to disastrous than perfect.

First of all, the code itself doesn't reflect your description. Your intentions are good, but your code doesn't reflect your intentions.

I know the Singleton design is supposing to have only one instance per "session", so I don't see the reason to dynamically allocate the object instead of pushing it onto the stack, and being destroyed automatically by the runtime library. This way no "memory management" will be needed for the Singleton design. Anyway I will skip that..

Step 5 - Introducing reference counters is interesting

The reference counting ... doesn't count.

Code:

Singleton* Singleton :: CreateSingleton()
{
      if (NULL = = InstSingleton_mp)
      {
          InstSingleton_mp = new Singleton();
          ++refCount;
      }
      return InstSingleton_mp;
}

if you increment the counter inside the condition if( ptr == NULL ) how will the counter increment when function is called. Will be "incremented" only when the function is called for the very first time.

Code:

Singleton :: DestroyMe()
{
      if( NULL ! =  InstSingleton_mp && 0 = = refCount)
    {
            detele  InstSingleton_mp;
            if(0 < refCount)
              --refCount;
            InstSingleton_mp=NULL;
    }
}

Now, here is the most interesting part. The reference counter will be decremented ONLY if it reaches 0.
Basically, NEVER.
Even if you will fix the IF condition clause, the reference is decremented only when the pointer is deleted and set to null. So only once per allocation.
In this scenario, your counter isn't a ref-counter, is more a redundant variable.

One more aspect that the "perfect" design is missing, is the thread safety of the singleton model.
You force the user to use the singleton instance from one thread only. If the user uses this model in a multi-threaded application it may collapse under allocation and deallocation routines which are not designed to support that.

Cheers

asadullah.ansari 24Jan2009 15:30

Re: Search for Robust Singleton Design Pattern
 
Yes!!! You are right...

Correct code will be as:


Step 5: Introducing reference counts (In our software a class is available for it)


Code:

class Singleton
{
      public:
          static Singleton * CreateSingleton();
          ~Singleton();
          void DestroyMe();
      private:
        static Singleton *instSingleton_mp;
        static int refCount;
        Singleton(){ ; }
        Singleton(const Singleton &) { ; }
};


//Definition
Singleton* Singleton :: InstSingleton_mp = NULL;
int Singleton :: refCount = 0;


Singleton* Singleton :: CreateSingleton()
{
      if (NULL == InstSingleton_mp)
      {
          InstSingleton_mp = new Singleton();
      }

      ++refCount;
      return InstSingleton_mp;
}


Singleton :: DestroyMe()
{

      --refCount;
      if( NULL !=  InstSingleton_mp && 0 == refCount)
    {
            detele  InstSingleton_mp;
            InstSingleton_mp=NULL;
    }
}


Code:

Singleton :: DestroyMe()
{

      if(0 < refCount)

        --refCount;
      if( NULL !=  InstSingleton_mp && 0 == refCount)
    {
            detele  InstSingleton_mp;
            InstSingleton_mp=NULL;
    }
}


zamjad 29Jan2009 21:33

Re: Search for Robust Singleton Design Pattern
 
Even if we leave thread safty for a while then there are still few problems here.

1. First you don't have to write empty body for copy ctor you can do the same thing with this

Code:

class Singleton
{
      public:
          static Singleton * CreateSingleton();
          ~Singleton();
          void DestroyMe();
      private:
        static Singleton *instSingleton_mp;
        static int refCount;
        Singleton(){}
        Singleton(const Singleton &);
};

2. Second in your this code

Quote:

Singleton :: DestroyMe()
{

if(0 < refCount)

--refCount;
if( NULL != InstSingleton_mp && 0 == refCount)
{
detele InstSingleton_mp;
InstSingleton_mp=NULL;
}
}
You are doing decrement in refCount variable only when it is less than zero (means you never did it).

3. Third the static member variable of Singlton has different name in class and in body, so this code wont even compile

Quote:

static Singleton *instSingleton_mp;
Quote:

InstSingleton_mp = new Singleton();
Quote:

Singleton* Singleton :: InstSingleton_mp = NULL;
4. Fourth return type is not defined for DestroyMe() function, so it wont compile. It should be like this

Code:

void Singleton :: DestroyMe()
5. Fifth thers is no keyword like "detele". It should be delete.

Code:

      if( NULL !=  InstSingleton_mp && 0 == refCount)
    {
            detele  InstSingleton_mp;
            InstSingleton_mp=NULL;
    }

6. Sixth you didnt define the body of dtoc so you will get linking error. It should be something like this

Code:

~Singleton() {};

7. Seventh and probably the most important one is what will be the situation when someone write a code something like this

Code:

Singleton* pInstance1 = Singleton::CreateSingleton();
Singleton* pInstance2 = pInstance1->CreateSingleton();

delete pInstance1;
delete pInstance2;

Because you didn't make your dtor private therefore your reference counting and even your application will break here.

Please at least compile the code before posting, so at least reader wont get at least compilation errors.

shabbir 4Feb2009 11:00

Re: Search for Robust Singleton Design Pattern
 
Nomination for article of the month for January 2009 Started

jayaraj_ev 5Feb2009 12:30

Re: Search for Robust Singleton Design Pattern
 
Quote:

Originally Posted by madlex (Post 41864)
Sorry for telling you that, but your "robust" singleton design is closer to disastrous than perfect.

First of all, the code itself doesn't reflect your description. Your intentions are good, but your code doesn't reflect your intentions.

I know the Singleton design is supposing to have only one instance per "session", so I don't see the reason to dynamically allocate the object instead of pushing it onto the stack, and being destroyed automatically by the runtime library. This way no "memory management" will be needed for the Singleton design. Anyway I will skip that..

Step 5 - Introducing reference counters is interesting

The reference counting ... doesn't count.

Code:

Singleton* Singleton :: CreateSingleton()
{
      if (NULL = = InstSingleton_mp)
      {
          InstSingleton_mp = new Singleton();
          ++refCount;
      }
      return InstSingleton_mp;
}

if you increment the counter inside the condition if( ptr == NULL ) how will the counter increment when function is called. Will be "incremented" only when the function is called for the very first time.

Code:

Singleton :: DestroyMe()
{
      if( NULL ! =  InstSingleton_mp && 0 = = refCount)
    {
            detele  InstSingleton_mp;
            if(0 < refCount)
              --refCount;
            InstSingleton_mp=NULL;
    }
}

Now, here is the most interesting part. The reference counter will be decremented ONLY if it reaches 0.
Basically, NEVER.
Even if you will fix the IF condition clause, the reference is decremented only when the pointer is deleted and set to null. So only once per allocation.
In this scenario, your counter isn't a ref-counter, is more a redundant variable.

One more aspect that the "perfect" design is missing, is the thread safety of the singleton model.
You force the user to use the singleton instance from one thread only. If the user uses this model in a multi-threaded application it may collapse under allocation and deallocation routines which are not designed to support that.

Cheers

Hi,
On your first Point mentioning that ....
"I know the Singleton design is supposing to have only one instance per "session", so I don't see the reason to dynamically allocate the object instead of pushing it onto the stack, and being destroyed automatically by the runtime library. This way no "memory management" will be needed for the Singleton design. Anyway I will skip that.".
As this class is singleton it will have only one instance...and How will you make sure that this having only one instance that is why we dynamically allocate memory for it on heap. so that the instance is presistent for all the functions or stacks created on your program.

And can you tell me where and how you can design a singleton class without using dynamic allcation.

jayaraj_ev 5Feb2009 12:32

Re: Search for Robust Singleton Design Pattern
 
Quote:

Originally Posted by asadullah.ansari (Post 41911)
Yes!!! You are right...

Correct code will be as:


Step 5: Introducing reference counts (In our software a class is available for it)


Code:

class Singleton
{
      public:
          static Singleton * CreateSingleton();
          ~Singleton();
          void DestroyMe();
      private:
        static Singleton *instSingleton_mp;
        static int refCount;
        Singleton(){ ; }
        Singleton(const Singleton &) { ; }
};


//Definition
Singleton* Singleton :: InstSingleton_mp = NULL;
int Singleton :: refCount = 0;


Singleton* Singleton :: CreateSingleton()
{
      if (NULL == InstSingleton_mp)
      {
          InstSingleton_mp = new Singleton();
      }

      ++refCount;
      return InstSingleton_mp;
}


Singleton :: DestroyMe()
{

      --refCount;
      if( NULL !=  InstSingleton_mp && 0 == refCount)
    {
            detele  InstSingleton_mp;
            InstSingleton_mp=NULL;
    }
}


Code:

Singleton :: DestroyMe()
{

      if(0 < refCount)

        --refCount;
      if( NULL !=  InstSingleton_mp && 0 == refCount)
    {
            detele  InstSingleton_mp;
            InstSingleton_mp=NULL;
    }
}




Hi,
You forgaot to add operator overloading for =() to add the counts and subtract the counts when they are assigned to NULL

zamjad 5Feb2009 18:13

Re: Search for Robust Singleton Design Pattern
 
Quote:

Originally Posted by jayaraj_ev (Post 42472)
Hi,
On your first Point mentioning that ....
"I know the Singleton design is supposing to have only one instance per "session", so I don't see the reason to dynamically allocate the object instead of pushing it onto the stack, and being destroyed automatically by the runtime library. This way no "memory management" will be needed for the Singleton design. Anyway I will skip that.".
As this class is singleton it will have only one instance...and How will you make sure that this having only one instance that is why we dynamically allocate memory for it on heap. so that the instance is presistent for all the functions or stacks created on your program.

And can you tell me where and how you can design a singleton class without using dynamic allcation.


Without going into advantages and disadvantages here is the simplest implementation of Singleton without using Heap

Code:

  Singleton* Singleton::CreateObject()
  {
    static Singleton obj;
    return &obj;
  }

This will be useful in single threaded application.

jayaraj_ev 5Feb2009 19:30

Re: Search for Robust Singleton Design Pattern
 
Quote:

Originally Posted by zamjad (Post 42498)
Without going into advantages and disadvantages here is the simplest implementation of Singleton without using Heap

Code:

  Singleton* Singleton::CreateObject()
  {
    static Singleton obj;
    return &obj;
  }

This will be useful in single threaded application.

Hi,
Good that u tried to do one without dynamic allocation. But one thing we should be carefull about is Static objects get created at start of program, so the objects gets created before main() which calls its constructor before. How we implement inside constructor also matters.

zamjad 5Feb2009 20:18

Re: Search for Robust Singleton Design Pattern
 
Quote:

Originally Posted by jayaraj_ev (Post 42499)
Hi,
Good that u tried to do one without dynamic allocation. But one thing we should be carefull about is Static objects get created at start of program, so the objects gets created before main() which calls its constructor before. How we implement inside constructor also matters.

That happens with only global static object. Here object will create only when you call this function first time.

In fact you can always make a program to test it. Here is the simple test program to verify this

Code:

#include <iostream>

using std::cout;
using std::endl;

class Singleton
{
private:
        Singleton()
        {
                cout << "Singleton::Singleton" << endl;
        }

        ~Singleton() { }

public:
        static Singleton* CreateObject()
        {
                static Singleton obj;
                return &obj;
        }
};

int main()
{
        return 0;
}



All times are GMT +5.5. The time now is 13:18.