/***************************************** singleton_test : This is a demonstration of a nifty solution to the tangled singleton problem. I'm working from the specific example in Alexandrescu (Modern C++ Design), the Keyboard/Log problem. The issue here goes like this : 1. Singletons should only be made one 2. They should go away at problem exit, to release whatever handles they might hold 3. If singletons use each other, then their lifetimes need to be correct based on their dependencies. In this example, the Keyboard makes use of the Log. (SKeyboard and SLog singletons). The tricky bit is that we want them to both release whatever handles they have to system resources, *and* they must go in order so that Log is destroyed after Keyboard, since Keyboard needs Log. So, this is very easy with reference counting. All we need is for the keyboard to hold a reference to the Log during its lifetime. This will ensure that the Log can't go away while the keyboard exists. This is very neatly packages up with a smart pointer. The Keyboard holds an opaque (non-typed) smart pointer to the Log just for the purpose of holding a ref here. (I prefer a smart pointer class to a manual Take/Free ref-count system). The thing that makes these guys singletons is that the classes have one ref held on them by a *static* which means they own one reference to themselves. This is part of the definition of a singleton, so it's good we have it. If you run this program you'll see something like this : CLog::Construct CKeyboard::Construct key : 0 key : 7 CKeyboard::Destruct CLog::Destruct The flow of execution is actually like this : CKeyboard::Instance make a CKeyboard ; CKeyboard::ref=1 in static CKeyboard makes a log CLog::Instance make a CLog ; CLog::ref = 1 in static CKeyboard takes ref on log; CLog::ref = 2 in static // do stuff atexit : CLog::spLog static destruction : CLog::ref = 1 CKeyboard::spKB static destruction : CKeyboard::ref = 0 CKeyboard destructs CKeyboard destruct of ref to CLog destructs : CLog::Ref = 0 CLog destructs ***************************************/ #include "gUtility.h" #include "gBase.h" #include //--------------------------------------------- namespace SLog { // Log singleton //----------------------------- // private part : gPtrDef(CLog); class CLog : public gBase { public: static CLog & Instance() { // smart pointer to constructed singleton // will be destructed at application exit // which will cause a ref-release // which will destruct CLog when no // more refs are on it static CLogPtr spLog( new CLog() ); gAssert(spLog != NULL); return *(spLog.GetPtr()); } void Log(const char * str) { gAssert(m_bIsValid); puts(str); } protected: CLog() { m_bIsValid = true; puts("CLog::Construct"); } ~CLog() { puts("CLog::Destruct"); m_bIsValid = false; } private: FORBID_CLASS_STANDARDS(CLog) bool m_bIsValid; // imaginary handle to log files here }; //----------------------------- // public part : gBasePtr GetPtr() { // return a gBasePtr just so people can // hold onto reference-counts of me return gBasePtr( &CLog::Instance() ); } void Log(const char * str) { CLog::Instance().Log(str); } }; //--------------------------------------------- namespace SKeyboard { // Keyboard singleton //----------------------------- // private part : gPtrDef(CKeyboard); class CKeyboard : public gBase { public: static CKeyboard & Instance() { // smart pointer to constructed singleton // will be destructed at application exit // which will cause a ref-release // which will destruct CKeyboard when no // more refs are on it static CKeyboardPtr spKB( new CKeyboard() ); gAssert(spKB != NULL); return *(spKB.GetPtr()); } void SetKey(int x) { m_key = x; } int GetKey() const { return m_key; } protected: CKeyboard() { m_key = 0; // hold a smart pointer to the log singleton // so that we make sure it hangs around longer than us : m_spLogRef = SLog::GetPtr(); SLog::Log("CKeyboard::Construct"); } ~CKeyboard() { SLog::Log("CKeyboard::Destruct"); } private: FORBID_CLASS_STANDARDS(CKeyboard) int m_key; gBasePtr m_spLogRef; // imaginary handle to the keyboard here }; //----------------------------- // public part : int GetKey(void) { return CKeyboard::Instance().GetKey(); } void SetKey(int x) { CKeyboard::Instance().SetKey(x); } }; //--------------------------------------------- void singleton_test() { int x = SKeyboard::GetKey(); printf("key : %d\n",x); SKeyboard::SetKey(7); x = SKeyboard::GetKey(); printf("key : %d\n",x); } //---------------------------------------------