Skip to main content
gamedev.net
🔒 Locked

Singleton woes

Started by erichardson 5 replies 1.2k views
Original Post
erichardson
erichardson
In order to get to the problem I will have to "set the scene" a little. Please bear with me... I have the following Singleton template class which resides in a static library (.lib).

template <typename T>
class CSingleton
{
protected:
	static T* m_Singleton;

public:
	// Constructor
	CSingleton()
	{
		assert(!m_Singleton);
#if defined( _MSC_VER ) && _MSC_VER < 1200
		int Offset = (int)(T*)1 - (int)(CSingleton<T>*)(T*)1;
		m_Singleton = (T*)((int)this + Offset);
#else
		m_Singleton = static_cast<T*>(this);
#endif
	}

	// Destructor
	~CSingleton()
	{
		assert(m_Singleton);
		m_Singleton = NULL;
	}

	static T& GetSingleton()
	{
		assert(m_Singleton);
		return *m_Singleton;
	}

	static T* GetSingletonPtr()
	{
		assert(m_Singleton);
		return m_Singleton;
	}
};

template <typename T> T* CSingleton <T>::m_Singleton = NULL;

In this static library I also have a CLogger class which inherits from this Singleton class. In my application (.exe) I link to this .lib and instantiate a CLogger class. The .exe also links to a .dll at runtime and this dll contains the following code which is causing me the problem...

CLogger::GetSingleton()->Log("D3D9Graphics.dll", "CD3D9Graphics", 
			"Init", "Failed to create Direct3DDevice9");

The problem is that the previous bit of code causes an assertion because the m_Singleton member is null. However, in the .exe, I have the following bit of code which does work as the log is created and written to...

m_Logger.GetSingleton().CreateLog("log.txt");

Can anyone explain to me why this is happening and how I might be able to solve this problem?
choffstein
choffstein
I think...

If you use the static method without initiating m_Singleton first, it will always be null. Therefore, what you should do is:

static T& GetSingleton(){   if(!m_Singleton){      m_Singleton = new T;   }}   return m_Singleton;


I suspect that if you create m_Logger then call CLogger statically, it would work fine, because m_Singleton would have been created. But because CLogger is called first, m_Singleton is null.

Something like that should work.

-visage
Fruny
Fruny
Templates can't reside in binary libraries. Only template instances, like CSingleton, where Foo is a real type, can. (So, in fact, CLogger should be fine).

Singletons are generally much more trouble than they are worth.

Have you checked that your offset trickery is legit?

Where's the code for CreateLog() and Log() ?

Why does one use . and the other ->, when GetSingleton() returns a reference?

What is m_Logger? What is its relation with CLogger?

Where is your CLogger instance created?
"Debugging is twice as hard as writing the code in the first place. Therefore, if you write the code as cleverly as possible, you are, by definition, not smart enough to debug it." — Brian W. Kernighan
choffstein
choffstein
Well, maybe he could just overload GetSingleton in CLogger? Something like:

static CLogger& GetSingleton(){   if(!m_Singleton)      m_Singleton = new CLogger;   return m_Singleton;}


This should work, right? Or maybe this snow day has just made my mind lazy.

You seem to be correcting me left and right here, Fruny ;)

[Edited by - visage on January 6, 2005 4:03:49 PM]
leedgitar
leedgitar
What does the header for CLogger look like? You might have to override the singleton retrieval method as such:

static CLogger& getSingleton(void);

[Edit] Oops, didn't refresh the page before replying and didn't see the prior response.
________________________________________________Chris McGuirkLead Programmer - Axiom 3D EngineC# gaming, the way of the future!http://www.axiom3d.org
erichardson
erichardson
Quote:

What does the header for CLogger look like?


class CLogger : public CSingleton<CLogger>{private:        std::ofstream m_Out;public:	// Constructor	CLogger();        CLogger(String szName);	// Destructor	~CLogger();	// Creates a new log	void CreateLog(String szName);        // Logs a message        void Log(String szModule, String szClass, String szFunction, String szMsg);};


Quote:

Have you checked that your offset trickery is legit?


I plead guilty. Although I don't follow this little bit of code, I have debugged the app and the m_Singleton variable gets initialised to something other than 0x000000 so I figured it would be working...

Quote:

Where's the code for CreateLog() and Log() ?


void CLogger::Log(String szModule, String szClass, String szFunction, String szMsg){	SYSTEMTIME dt; 	GetSystemTime(&dt);	m_Out << dt.wDay << "/" << dt.wMonth << "/" << dt.wYear << " " <<			 dt.wHour << ":" << dt.wMinute << ":" << dt.wSecond << " (" <<			 szModule << ": " << szClass << ": " << szFunction << ") " <<			 szMsg << std::endl;}void CLogger::CreateLog(String szName){	m_Out.open(szName.c_str(), std::ios::out);	SYSTEMTIME dt; 	GetSystemTime(&dt);	m_Out << dt.wDay << "/" << dt.wMonth << "/" << dt.wYear << " " <<			 dt.wHour << ":" << dt.wMinute << ":" << dt.wSecond << 			 " Log created" << std::endl;}


Quote:

Why does one use . and the other ->, when GetSingleton() returns a reference?


That's a typo on my part while I was making this post [lol]

Quote:

What is m_Logger? What is its relation with CLogger?

Where is your CLogger instance created?


m_Logger is just what I called an instance of CLogger. m_ because it is a member variable of my application class. It is created when the application is started.

Quote:

Singletons are generally much more trouble than they are worth.


I'm starting to agree...[smile]



corliss
corliss
If you include a getSingleton method in the CLogger class it is not overriding but hiding the Singleton's method since you can not override static methods. In this case it may not be too bad but something to look out for.

Topic Locked

This topic has been locked by a moderator. New replies are not allowed.

Sign in to reply to this topic.