This section of the archives stores flipcode's complete Developer Toolbox collection, featuring a variety of mini-articles and source code contributions from our readers.

| Using Singletons Submitted by

This isn't specifically a game programming item, but all the same, I have found it incredibly useful in all my projects, both game related, and more mundane. Over and over, I have been told that using globals in code is very bad thing, and under most circumstances, this is true. However, there are some cases where a global can be incredibly useful... what do you do then? Ive found code that uses globals, is almost impossible to full understand, when someone else wrote it (flashbacks to reading truly horrible C code when I was just learning to program). Additionally, verifying that the code has been properly initialized, and is in proper form can quickly become a nightmare and prone to errors. This is where singletons come in handy.
What is a singleton? In a nutshell, a singleton is a global object, that provides a mechanism to guarantee only one instance is EVER initialized. Additionally, a singleton provides an interface, that protects against improper initialization (i.e., using a pointer before its new'ed). How? Actually, its fairly simple...

    class MySingleton
    {
    public:
    	MySingleton * GetInstance()
    				{
    				if(pInstance) return pInstance;
    // If already declared, return existing reference
    				else return (pInstance = new MySingleton);
    // Otherwise, make a new one
    				}
    	Do() { cout << "Did something" << endl; }
    private:
    	MySingleton(){}; // Make Constructor private so it can only be
    accessed via GetInstance()
    	static MySingleton * pInstance;
    };

Thats it... well, more or less thats it... since its static, pInstance must be declared in the global scope (pick a cpp file)...
so, In MySingleton.cpp insert the line

    ...
    static MySingleton * MySingleton::pInstance = NULL;
    ...

Now, in order to use...

    #include "MySingleton.h"

    void main(void)
    {
    	MySingleton::GetInstance()-Do();
    	//or
    	MySingleton * pSing = MySingleton::GetInstance();
    	pSing-Do(); // Uses exact same instance as the first line
    }

To date this is the safest method ive found of creating a single object across a global scope... and to boot... its easy :) A few downside exist though, the biggest of which being its global, so you really dont have any choice if you want one or not... your always going to have one instance.. but generally, this is a requirement for the objects im using anyways. To date, ive implemented Video classes this way, Sound, etc... and Ive loved it. Ive attached a sample simple singleton class I use for file tracing (wow, we have alot of these now eh? :) to illustrate how this works(the code is by now means beautiful)... hope you enjoy.

Currently browsing [ftrace.zip] (1,893 bytes) - [FTrace.h] - (1,383 bytes)

    #ifndef _F__TRACE_H_
    #define _F__TRACE_H_

    #include <stdio.h>
    #include <stdlib.h>
    #include <io.h>
    #include <assert.h>
    //#include <iostream>
    #include <fstream>
    #include <windows.h> // Required before including winbase :(
    #include <winbase.h> // For SYSTEMTIME

    #include "file/file.h"
    #define LOGFILENAME ".\\log.txt"

    //////////////////////////////////////////////////////////////
    #ifndef NOTRACE
    	#define PUSHSTACK(x)			StackTrace st(x,__FILE__,__LINE__);
    	#define TRACE(x)				FTrace::GetInstance()->Log(x);
    #define STOP_TRACE() 		delete FTrace::GetInstance();
    #else
    	#define PUSHSTACK(x)
    	#define TRACE(x)
    #endif
    //////////////////////////////////////////////////////////////

    using namespace std;

    class FTrace
    {
    public:

    	static FTrace * GetInstance();

    	static void IncrementStackDepth() { m_nCurStackDepth++; }
    	static void DecrementStackDepth() { m_nCurStackDepth--; }
    	static int  GetStackDepth()		  { return m_nCurStackDepth; }

    	void Log(const char*);

    	~FTrace();

    private:
    	FTrace();
    	File * logFile;
    	static FTrace * pivInstance;

    	static int m_nCurStackDepth;

    	void GetDateString(char * destString);

    };

    class StackTrace
    {
    public:
    	StackTrace(const char*,const char* szFileName="unknown", int nLine = 42);

    	~StackTrace();

    private:
    	char m_szFuncName[100];
    	SYSTEMTIME m_StartTime;
    };

    #endif

Currently browsing [ftrace.zip] (1,893 bytes) - [FTrace.cpp] - (3,249 bytes)

    #include "Debug/FTrace.h"

    // ******************************************************************************
    // **					FTrace: Log to file singleton class
    // ******************************************************************************

    FTrace * FTrace::pivInstance;
    int FTrace::m_nCurStackDepth;

    FTrace::FTrace()
    {
    	pivInstance = 0;
    	m_nCurStackDepth = 0;

    	logFile = new File;
    	logFile->Open(LOGFILENAME,FILE_TEXTREADWRITE);
    	Log("****** Started ******");
    }

    FTrace::~FTrace()
    {
    	m_nCurStackDepth =0;
    	Log("****** Ended ******");
    	delete logFile;

    }

    FTrace * FTrace::GetInstance()
    {
    	if(pivInstance != 0)
    		return pivInstance;
    	return FTrace::pivInstance = new FTrace;
    }

    // Dest must already be allocated
    void FTrace::GetDateString(char * dest)
    {
    	dest[0] = 0;
    	char lpTempBuf[255]; lpTempBuf[0];
    	SYSTEMTIME  sysTime;
    	GetLocalTime(&sysTime);
    	GetDateFormat(LOCALE_SYSTEM_DEFAULT,
    					DATE_SHORTDATE,
    					&sysTime,
    					NULL,
    					lpTempBuf,
    					250);
    	sprintf(dest,"%s %d:%d %d:%d",lpTempBuf,sysTime.wHour,
    									sysTime.wMinute,sysTime.wSecond,sysTime.wMilliseconds);
    }

    void FTrace::Log(const char * logString)
    {
    	assert(logString != NULL);

    		char * curDate; curDate = new char[255];
    		GetDateString(curDate);
    		for(int i = 0 ; i < m_nCurStackDepth;i++)
    			logFile->WriteChar(' ');

    		logFile->WriteChar('[');
    		char hack[10]; hack[0] = 0;
    		sprintf(hack,"%d",m_nCurStackDepth);
    		logFile->WriteString(hack);

    		logFile->WriteChar(']');
    		logFile->WriteChar(' ');
    		logFile->WriteString(curDate);
    		logFile->WriteChar(' ');
    		logFile->WriteString(logString);
    		logFile->WriteChar('\n');
    		delete curDate;
    }

    // ******************************************************************************
    // **					StackTrace: Stack based logging routine
    // **						has dependancies on FTrace
    // ******************************************************************************

    // *********************************
    // StackTrace is a helper class to FTrace that autoterminates on function termination
    // Using contructor and destructor to generate start/stop logs, as well as recording
    // the current depth of the trace

    StackTrace::StackTrace(const char * szFuncName,const char* szFileName,int nLine)
    {
    	FTrace * ft = FTrace::GetInstance();

    	ft->IncrementStackDepth();

    	m_szFuncName[0] = NULL;

    	strcpy(m_szFuncName,szFuncName);

    	char debugString[256];
    	debugString[0] = 0;

    	strcpy(debugString,"[START]\tEntered function: ");
    	strcat(debugString,m_szFuncName);
    	sprintf(debugString,"%s in %s on line %d",debugString,szFileName,nLine);

    	ft->Log(debugString);
    	GetLocalTime(&m_StartTime);
    }

    StackTrace::~StackTrace()
    {
    	FTrace * ft = FTrace::GetInstance();

    	WORD totalTime;

    	SYSTEMTIME elapsedTime;
    	GetLocalTime(&elapsedTime);

    	totalTime = ((elapsedTime.wSecond - m_StartTime.wSecond) * 1000) + (elapsedTime.wMilliseconds - m_StartTime.wMilliseconds);
    	char debugString[256];
    	debugString[0] = 0;
    	sprintf(debugString,"[STOP] (%d ms)\tExiting function: ",totalTime);

    	if(m_szFuncName != NULL)
    	{
    		strcat(debugString,m_szFuncName);
    		ft->Log(debugString);

    	}

    	ft->DecrementStackDepth();
    }

The zip file viewer built into the Developer Toolbox made use of the zlib library, as well as the zlibdll source additions.

Copyright 1999-2008 (C) FLIPCODE.COM and/or the original content author(s). All rights reserved.
Please read our Terms, Conditions, and Privacy information.