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