Sign in to follow this  

Singleton problems

This topic is 4490 days old which is more than the 365 day threshold we allow for new replies. Please post a new topic.

If you intended to correct an error in the post then please contact us.

Recommended Posts

Hi, im having a little trouble with writing a simple singleton. My implementation is as follows:
// Singleton.h
#pragma once

template <class T>
class CSingleton
{
  public:
    static T* CreateInstance(void);
    static void DeleteInstance(void);

    static T* GetInstancePtr(void);
    static T& GetInstanceRef(void);
    
  private:
    CSingleton(void);
    CSingleton(const CSingleton& rCopy);
    CSingleton& operator=(const CSingleton& rCopy);
    ~CSingleton(void);

    static T* m_pT;
};

template <class T>
T* CSingleton<T>::m_pT = NULL;

template <class T>
CSingleton<T>::CSingleton(void)
{
}

template <class T>
CSingleton<T>::~CSingleton(void)
{
}

template <class T>
T* CSingleton<T>::CreateInstance(void)
{
  if (m_pT == NULL)
    m_pT = new T();

  return m_pT;
}

template <class T>
void CSingleton<T>::DeleteInstance(void)
{
  if (m_pT != NULL)
  {
    delete m_pT;
    m_pT = NULL;
  }
}

template <class T>
T* CSingleton<T>::GetInstancePtr(void)
{
  return m_pT;
}

template <class T>
T& CSingleton<T>::GetInstanceRef(void)
{
  return *m_pT;
}

But it has lead to problems when trying to make a logging class a singleton:
// Log.h
#pragma once

class CLog : public CSingleton<CLog>
{
  public:
    CLog(void);
    ~CLog(void);
};

// Engine.cpp
...
  CLog* pLog = CLog::GetInstancePtr();
  CLog* pLog2 = new CLog(); // Test to make sure that the user cant make a new object
...

The compiler moans that it cant access the private members __ctor and dtor in the CLogs constructor and destructor. Which obviously makes sense, but if i dont have them as private, the developer is able to call "new CLog()" which means it breaks the singleton rules. Can anyone give me any pointers where im going wrong?

Share this post


Link to post
Share on other sites
So basically you took precautions for not allowing the user to create instances of the class using new. Subsequently you tried to create a new instance using new and the compiler complains.

I'd say all is fine! In more detail: you don't want to expose the constructor of the derived class either.

Greetz,

Illco

Share this post


Link to post
Share on other sites
Yeah thats the thing, i dont really want to mess around with all the child classes, i want to disable the user using new on the class without changing the original implementation except for inheriting from CSingleton.

BTW: The compiler complains from within the CLog() & ~CLog() saying it cant access the CSingleton constructor / destructor. But this *isnt* as a result of the code in Engine.cpp - its just as it tries to compile log.cpp.

Share this post


Link to post
Share on other sites
I know. If the parent constructor is private you cannot call it in a derived class. I know see your problem appear: you want to forbid the user to even derive such a class or, on the other hand, make sure its constructors are indeed private. I'm not into templates enough to help you there; though I doubt it is possible to create a nice singleton base class using templates.

Share this post


Link to post
Share on other sites
yeah, that would be a real pain having a whole list of friend classes inside the singleton template. ive resorted to using the following technique:


#define CLASS_SINGLETON(CLASS) CLASS : public CSingleton<CLASS>
#define IMPLEMENT_SINGLETON(CLASS) friend CSingleton<CLASS>; private: CLASS(void); CLASS(const CLASS&); CLASS& operator=(const CLASS&); ~CLASS(void);



...and...

class CLASS_SINGLETON(CLog)
{
IMPLEMENT_SINGLETON(CLog)
};




But im a bit dubious about using a define to create constructors for a class - it might not be very clear - is it better to just write them all out by hand each time?

Share this post


Link to post
Share on other sites

template<class T>
class singleton
{
public:
static T& instance()
{
if(!_instance)
{
_instance = new T;
}

return _instance;
}
private:
static T* _instance;
};




Then inherit your derviced class D from singleton<D> to make it a singleton.

However, the derived class must make its constructor private and must make singleton<D> a friend class. There is no way around this. The above is as clean as you're going to get.

NOTE:
The above is just a skeleton. Don't use it as is.

Share this post


Link to post
Share on other sites
ok, ive rewritten it slightly:


// Singleton.h
#pragma once

template <class T>
class CSingleton
{
public:
static T* GetInstancePtr(void);
static T& GetInstanceRef(void);

protected:
CSingleton(void);
~CSingleton(void);

private:
CSingleton(const CSingleton& rCopy);
CSingleton& operator=(const CSingleton& rCopy);

static T* CreateInstance(void);
static void DeleteInstance(void);

static T* m_pT;
};

template <class T>
T* CSingleton<T>::m_pT = NULL;

template <class T>
CSingleton<T>::CSingleton(void)
{
}

template <class T>
CSingleton<T>::~CSingleton(void)
{
}

template <class T>
T* CSingleton<T>::CreateInstance(void)
{
if (m_pT == NULL)
{
m_pT = new T();
atexit(CSingleton<T>::DeleteInstance);
}

return m_pT;
}

template <class T>
void CSingleton<T>::DeleteInstance(void)
{
if (m_pT != NULL)
{
delete m_pT;
m_pT = NULL;
}
}

template <class T>
T* CSingleton<T>::GetInstancePtr(void)
{
if (m_pT == NULL)
CSingleton<T>::CreateInstance();

return m_pT;
}

template <class T>
T& CSingleton<T>::GetInstanceRef(void)
{
if (m_pT == NULL)
CSingleton<T>::CreateInstance();

return *m_pT;
}




// Log.h
#pragma once

class CLog : public CSingleton<CLog>
{
friend CSingleton<CLog>;

private:
CLog(void);
CLog(const CLog&);
CLog& operator=(const CLog&);
~CLog(void);
};

// Engine.cpp
...
CLog* pLog = CLog::GetInstancePtr();
//CLog* pLog2 = new CLog(); // This doesnt work - yay!
...



Any advances on this? Seems to work, no memory leaks or anything, memory is cleared up during atexit().

Share this post


Link to post
Share on other sites
from what i read that is a bad / not so good idea as you have no idea when that pointer gets free'd? at least using the atexit() function will free it last - after all other global objects? (or am i getting confused...)

i was trying to get around the destruction order problem of:

class A init -> class Log init -> class B init | CRASH | class B del -> class Log del -> class A del.

As the log class gets deleted before class A does, when GetInstance() is called, it will recreate another object? whereas if you use the atexit function it will be deleted after all the other objects?



Share this post


Link to post
Share on other sites
Quote:
Original post by Illco
That would work but defeats the purpose as it requires the templated base class to know about its inheritors.

No it doesn't.

Share this post


Link to post
Share on other sites
No Rebooted is right in correcting me -- I thought at first he meant making the log class a friend of the base class but that was not what he had written. It is the other way around.

I still think all this trouble isn't worth it -- the singleton pattern is so simple and since you still end up having to guard it yourself using protected or private constructors it seems like a waste to me.

Illco

Share this post


Link to post
Share on other sites
When I use a singleton, which is rare, I use Loki::SingletonHolder, and don't forcibly stop users from directly creating an instance of the class, just advise them against it. This separates the singleton pattern from the class itself. I'd recommend Loki's SingletonHolder for a real project.

Share this post


Link to post
Share on other sites
Guest Anonymous Poster

Make the singleton derive privately from the type to be made into a singleton and that type's ctors can be made protected. No need for friend declarations.


template
struct singleton : private T {
static T* get();
};

struct a {
protected:
a();
};

typedef singleton a_t;



// ville

Share this post


Link to post
Share on other sites
Someone said that using templates could not impletemnt singeltons . I do not know where this came from as:

[source}
template<typename T>
class Singleton
{
static T* ms_singleton;
public:
Singleton()
{
assert(!ms_singleton);
//use a cunning trick to get the singleton pointing to the start of
//the whole, rather than the start of the Singleton part of the object
int offset = (int)(T*)1 - (int)(Singleton <T>*)(T*)1;
ms_singleton = (T*)((int)this + offset);
}
~Singleton()
{
assert(ms_singleton);
ms_singleton=0;
}
static T& GetSingleton()
{
assert(ms_singleton);
return *ms_singleton;
}
static T* GetSingletonPtr()
{
assert(ms_singleton);
return ms_singleton;
}
};

template <typename T> T* Singleton <T>::ms_singleton = 0;

[/source}


Now igonre the cunning bit!!!! Singleton, template!!! Now if you want to derive from it:

To use the singleton class, we derive a class SomeClass from

Singleton<SomeClass>.

One thing to note about this type of singleton is that we - not the loader - are responsible for creating the singleton and destroying it again when we're done. We create it simply by calling new SomeClass() somewhere in code - the constructor takes care of the rest, so we don't even need to store the pointer that new returns. To destroy it, we call delete SomeClass::GetSingletonPtr(); that also sets the singleton pointer back to zero, so we can recreate the singleton if we want.

Enginuity - Game dev (Superpig)

Am sorry but it works..:) Or read Andrei Alexandrecu (Modern C++ Design) which gives many ipmlementtions for a singleton...the easiest being the phoenix.

Share this post


Link to post
Share on other sites
A singleton is just a global with a funny name that makes you feel somewhat less queasy about using it. Generally, the monostate pattern is much clearer, and much less verbose.

Share this post


Link to post
Share on other sites

This topic is 4490 days old which is more than the 365 day threshold we allow for new replies. Please post a new topic.

If you intended to correct an error in the post then please contact us.

Create an account or sign in to comment

You need to be a member in order to leave a comment

Create an account

Sign up for a new account in our community. It's easy!

Register a new account

Sign in

Already have an account? Sign in here.

Sign In Now

Sign in to follow this