Contents
This article builds on the ideas of the famous Patterns book: "Design Patterns, Elements of Reusable Object-Oriented Software" [^] by the so called Gang of Four. That's this GOF[^], not this one[^] . I don't want to post text from, or a plug for, the book here, so while this article should be useful even if you haven't read the book, it will probably make a lot more sense if you have.
Having read this extremely influential book a few years ago, I wondered whether a number of the creational patterns, in particular, could be abstracted in such a way that most of the work would be taken out of using them for future classes.
Some may feel that what I have done is over complex in introducing class templates, or not in the spirit of the book, because my classes do not map one to one to those represented in the book's examples. I believe that these patterns are just that, the shapes of generic solutions, and the exact mapping of patterns into classes is not a point of principle. Also, I find it easier to make use of a working piece of generic code than an indirectly reusable specific example of a good idea.
What I really wanted to start with was to be able to make my new class a singleton simply by adding a DECLARE_SINGLETON
macro to the header and an IMPLEMENT_SINGLETON
macro to the .cpp file, in much the same style as MFC or ATL. I present here a way to do precisely that and a little more. The infrastructure required to make this possible also provides an opportunity to extend the catalogue of creational patterns with a new pattern I call a Flyer.
Reference will be made to my previous article[^] which introduced the static Templated Object Block (sTOB), not perhaps a pattern in itself, but useful in the same area of 'class instancing' as the creational patterns. I use sTOB(s) in this code and elsewhere to prevent static initialisation order problems occurring when the code is reused for purposes I hadn't thought of when I wrote it.
The templates and classes presented are intentionally simplified. A more complete implementation would probably use STL data structures and allocators, much more synchronisation security, and error checking.
Lots of Singleton implementations [^] have been published before, to the point where I hesitate to even include the code here. I do so only to show how the macros work which allow the features of a Singleton to be attached to a new class.
template< class T >
class CTSingleton
{
public:
static inline T* Instance();
static inline void Release();
protected:
CTSingleton(){} virtual ~CTSingleton(){}
private:
Declare_pseudo_static( T* ) _ppInstance;
Declare_pseudo_static( long ) plInstanceCount;
Declare_pseudo_static( CCriticalSection ) pCS;
};
template< class T > inline T* CTSingleton< T >::Instance()
{
pCS->Lock();
if( ((T*)_ppInstance) == 0 )
{
_ppInstance = new T();
}
(*plInstanceCount)++;
pCS->Unlock();
return ((T*)_ppInstance);
}
template< class T > inline void CTSingleton< T >::Release()
{
pCS->Lock();
(*plInstanceCount)--;
if( (*plInstanceCount) < 0 )
{
delete ((T*)_ppInstance);
((T*)_ppInstance) = 0;
(*plInstanceCount) = 0;
}
pCS->Unlock();
}
The only thing to note here beyond the use of sTOBs and the simple thread synchronisation is that these singletons are designed to be freed on the first unbalanced call to Release
; in other words, when the reference count goes to -1 rather than when it goes to 0. This prevents heap thrashing in single-threaded one-use-at-a-time scenarios, and allows the developer precise control over the point of destruction.
Now to the MFC style macros:
#define DECLARE_SINGLETON( _Class )\
\
friend CTSingleton< _Class >;\
protected:\
_Class();\
virtual ~##_Class##()
All this really does is prevent _Class
from being created except by derived classes or CTSingleton< _Class >
.
#define IMPLEMENT_SINGLETON( _Class )\
\
Implement_pseudo_static(_Class##*)\
CTSingleton< _Class >::_ppInstance;\
Implement_pseudo_static(long)\
CTSingleton< _Class >::plInstanceCount;\
Implement_pseudo_static(CCriticalSection)\
CTSingleton< _Class >::pCS
This goes in the .cpp implementation file for the class being made a singleton, and initialises the static data.
When you come to use a Singleton, you need an easy way to get a pointer to it. To that end and to help make reference counting easy, I wrote CTSingletonPtr
. It's a simple smart pointer class that wraps getting and releasing a CTSingleton
.
template<class T >
class CTSingletonPtr
{
private:
void* operator new(unsigned int){}
void operator delete(void*){}
protected:
T* m_pT;
public:
CTSingletonPtr()
{
m_pT = CTSingleton<T>::Instance();
}
virtual ~CTSingletonPtr()
{
m_pT = 0;
CTSingleton<T>::Release();
}
inline T* operator ->()
{
return m_pT;
}
inline T& operator()()
{
return *m_pT;
}
};
CTSingletonPtr<TSing> pSing;
gets you a pointer that can be used just like any other.
I implement these two very similar concepts with two types of classes. A ClassInstanceFactory
which, at its simplest, fulfills the role of a Factory Method, and rtabsfactory
which, at its simplest, fulfills the role of the Abstract Factory.
The class instance factory concept is so useful and flexible that I use it to implement most of the other patterns.
This comes in two parts. The first is a hierarchy of instance factory classes, starting with an abstract base. The second is a class registry which associates unique class identifiers with the factory which creates that class. In order to make the class registration system work, a way of attaching unique identifiers to classes is needed. I use a technique called 'poor man's RTTI' for this.
First, a GUID structure just like COM:
typedef struct __mxGUID
{
unsigned long int Data1;
unsigned short int Data2;
unsigned short int Data3;
unsigned char Data4[8];
}const mxGUID, *mxPGUID;
Then, a macro to add a GUID to a class.
#define DECLARE_OCLASS_ID(myClass)\
\
static const mxGUID* myClass::ClassID(void);\
static const char* myClass::TypeName(void)
Then, a macro to provide a default implementation of the GUID for a class:
#define IMPLEMENT_OCLASS_LUID(myClass)\
\
const mxGUID* myClass::ClassID()\
{\
static const mxGUID classID = \
{0x0000, 0x00, 0x00,\
{0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0}} ;\
return &classID;\
}\
\
const char* myClass::TypeName()\
{\
return #myClass ;\
}
This default implementation leaves the GUID value at 0, but that doesn't matter as we'd only need a real value if this class was somehow accessible externally to the process. Within the process, the address of this structure is sufficiently unique to behave as a Locally Unique Identifier (LUID), which is what we need.
Here is the abstract base class for instance factories:
class CClassInstanceFactory
{
public:
CClassInstanceFactory()
{
}
virtual ~CClassInstanceFactory()
{
}
virtual void* Instance() = 0;
virtual void Release(void* pInstance) = 0;
};
Now the class registry itself:
class CClassReg
{
DECLARE_SINGLETON( CClassReg );
protected:
CTMap<mxPGUID, CClassInstanceFactory*> m_RegMap;
typedef CTMap<mxPGUID, CClassInstanceFactory*>::TItem TItem;
public:
void Register(mxPGUID ClassID, CClassInstanceFactory* pFactory);
void UnRegister(mxPGUID ClassID);
CClassInstanceFactory* GetFactory(mxPGUID ClassID);
};
IMPLEMENT_SINGLETON( CClassReg );
CClassReg::CClassReg()
{
}
CClassReg::~CClassReg()
{
}
void CClassReg::Register(mxPGUID ClassID, CClassInstanceFactory* pFactory)
{
TItem item(ClassID, pFactory);
m_RegMap.Append(item);
}
void CClassReg::UnRegister(mxPGUID ClassID)
{
TItem item(ClassID, NULL);
m_RegMap.RemoveAt(m_RegMap.Find(item));
}
CClassInstanceFactory* CClassReg::GetFactory(mxPGUID ClassID)
{
TItem item(ClassID, NULL);
return m_RegMap[m_RegMap.Find(item)].Second();
}
Note that the Class Registry itself is a Singleton.
Now, we can create a simple override of CClassInstanceFactory
, which can be attached to something we want to create.
template<class T>
class CTClassRegEntry : protected CClassInstanceFactory
{
public:
CTClassRegEntry()
{
CTSingletonPtr<CClassReg> pReg;
pReg->Register(T::ClassID(), this);
}
~CTClassRegEntry()
{
CTSingletonPtr<CClassReg> pReg;
pReg->UnRegister(T::ClassID());
}
void* Instance()
{
return new T;
}
virtual void Release(void* pInstance)
{
T* pT = reinterpret_cast<T*>(pInstance);
delete pT;
}
};
This class can be used directly as a class factory, or as a base class for deriving different kinds of factory.
In the sample code, you'll also find a CTInstancePtr<T>
smart pointer class, very similar to CTSingletonPtr<T>
described above. This can be used to get a pointer to an instance of any class that registers a CClassInstanceFactory
derived factory with the CClassReg
registry.
A class to implement the Abstract Factory Pattern in a way that means it can be configured at runtime:
class CRTAbsFactory
{
protected:
CTMap<mxPGUID, mxPGUID> m_IDMap;
typedef CTMap<mxPGUID, mxPGUID>::TItem TItem;
public:
CRTAbsFactory()
{
}
virtual ~CRTAbsFactory()
{
}
template<class Tsubs, class Torg>
mxPGUID Override(Tsubs*, Torg*)
{
mxPGUID PrevClassID;
mxPGUID BaseClassID = Torg::ClassID();
mxPGUID SubClassID = Tsubs::ClassID();
TItem item(BaseClassID, 0);
TItem& Oitem = m_IDMap[m_IDMap.Find(item)];
PrevClassID = Oitem.Second();
Oitem.Second() = SubClassID;
return PrevClassID;
}
template<class Torg>
void Override( mxPGUID ClassID, Torg* )
{
mxPGUID BaseClassID = Torg::ClassID();
TItem item(BaseClassID, 0);
TItem& Oitem = m_IDMap[m_IDMap.Find(item)];
Oitem.Second() = ClassID;
}
template<class Tsubs, class Torg>
void Configure(Tsubs*, Torg*)
{
mxPGUID BaseClassID = Torg::ClassID();
mxPGUID SubClassID = Tsubs::ClassID();
TItem item(BaseClassID, SubClassID);
m_IDMap.Append(item);
}
void Configure( TItem item )
{
m_IDMap[m_IDMap.Find(item)].Second() = item.Second();
}
template<class Torg>
TItem GetConfig(Torg*)
{
mxPGUID BaseClassID = Torg::ClassID();
TItem item(BaseClassID, 0);
return m_IDMap[m_IDMap.Find(item)];
}
template<class T>
void Instance(T*& pT)
{
CTSingletonPtr<CClassReg> pReg;
mxPGUID BaseClassID = T::ClassID();
TItem item(BaseClassID, 0);
mxPGUID SubClassID = m_IDMap[m_IDMap.Find(item)].Second();
pT = reinterpret_cast<T*>(pReg->GetFactory(SubClassID)->Instance());
}
};
By using a class ID to class ID map, we can get the factory to produce any subclass when Instance
is called for the base class.
The Prototype pattern is achieved by replacing the basic TClassRegEntry
class with one that constructs all new instances by copying a prototype. Clearly, T
must be copy constructable.
template<class T>
class CProtoRegEntry : public CTClassRegEntry<T>
{
public:
static T Prototype;
CProtoRegEntry()
{
}
~CProtoRegEntry()
{
}
void* Instance()
{
return new T(Prototype);
}
};
To make use of this, we need a few more macros:
#define DECLARE_CLASS_PROTOTYPE(myClass)\
\
public:\
DECLARE_OCLASS_ID(myClass);\
static CProtoRegEntry< myClass > RegEntry
#define PROTO(myClass) CProtoRegEntry< myClass >::Prototype
#define IMPLEMENT_CLASS_PROTOTYPE(myClass)\
\
IMPLEMENT_OCLASS_LUID(myClass);\
CProtoRegEntry< myClass > myClass::RegEntry;\
myClass PROTO(myClass)
The approach used to achieve the Prototype pattern can also be used to implement Object Pooling. Many kinds of Object Pooling are possible. A very simplistic example is presented here.
template<class T>
class CTClassPoolRegEntry : public CTClassRegEntry<T>
{
friend T; protected:
class CPoolEntry
{
public:
T* m_pT; unsigned int m_uRefCount;
CPoolEntry()
{
m_pT = 0;
m_uRefCount = 0;
}
CPoolEntry(const CPoolEntry& src)
{
m_pT = src.m_pT;
m_uRefCount = 0;
}
~CPoolEntry()
{
delete m_pT;
}
CPoolEntry& operator = (const CPoolEntry& src)
{
if(m_pT != src.m_pT)
{
delete m_pT;
}
m_pT = src.m_pT;
m_uRefCount = src.m_uRefCount;
return *this;
}
unsigned int AddRef()
{
return ++m_uRefCount;
}
unsigned int Release()
{
return --m_uRefCount;
}
T* Item()
{
if(!m_pT)
{
m_pT = new T; }
return m_pT;
}
};
CTArray<CPoolEntry> m_aPool; int m_iReuse;
public:
CTClassPoolRegEntry(unsigned int uEstPoolSize) : CTClassRegEntry<T>()
{
m_aPool.SetCapacity(uEstPoolSize);
m_iReuse = -1;
}
~CTClassPoolRegEntry()
{
T* pT = NULL;
unsigned int uCount;
for(uCount = 0; uCount < static_cast<unsigned int>(m_aPool.Size()); uCount++)
{
pT = m_aPool[uCount].Item();
delete pT;
}
m_aPool.Free();
}
void* Instance()
{
T* pT = NULL;
unsigned int uCount;
if(m_iReuse >= 0) {
pT = m_aPool[static_cast<unsigned int>(m_iReuse)].Item();
m_iReuse = -1;
}
else {
for(uCount = 0; uCount < static_cast<unsigned int>(m_aPool.Size()); uCount++)
{
if(m_aPool[uCount].m_uRefCount == 0)
{
m_aPool[uCount].AddRef()
pT = m_aPool[uCount].Item();
break;
}
}
}
if(pT == NULL)
{
CPoolEntry entry;
entry.m_uRefCount = 1;
m_aPool.Append(entry);
pT = m_aPool[m_aPool.Size() - 1].Item();
}
return pT;
}
void Release(void* pInstance)
{
unsigned int uCount;
for(uCount = 0; uCount < static_cast<unsigned int>(m_aPool.Size()); uCount++)
{
if(m_aPool[uCount].m_pT == pInstance)
{
if(m_aPool[uCount].Release() <= 0)
{
m_iReuse = static_cast<int>(uCount);
}
break;
}
}
}
};
The builder template presented is a very thin wrapper which abstracts the small common elements of Builder Pattern implementations.
template< class TBuilder, class TProduct >
class CTBuilder : public TBuilder
{
public:
CTBuilder()
{
}
virtual ~CTBuilder()
{
}
TProduct* Instance(void)
{
TProduct* pProduct = new TProduct;
Construct( pProduct );
return pProduct;
}
virtual void Release( TProduct* pInstance )
{
delete pInstance;
}
};
The TBuilder
template parameter must be a concrete builder class with the relevant Construct
function.
The Flyer pattern, or Flyer instancing as I prefer to call it, is a new mode for the creation of and access to objects, which is made possible by the infrastructure I have described for implementing constructional patterns. Flyers are stack based objects, where the most recently created instance is always available to the current scope. They appear to fly up the stack without needing to be passed from function to function.
As a pattern, Flyer is a kind of per thread Singleton in that there is only ever one instance of any Flyer type accessible at any given point on any given thread.
Flyer requires a thread aware runtime class factory so that new instances can replace old ones at the top of a notional stack until they are destructed. This also allows 'overriding' of Flyers at runtime with derived classes.
First then, a Threaded Runtime Abstract Factory:
class CThreadedRTAbsFactory
{
protected:
typedef CTMap<mxPGUID, mxPGUID> GUIDMap;
typedef GUIDMap::TItem TGUIDItem;
typedef CTMap<DWORD, GUIDMap >::TItem TThreadItem;
CTMap<DWORD, GUIDMap > m_ThreadMap;
GUIDMap& GetMap()
{
TThreadItem tItem;
tItem.First() = ::GetCurrentThreadId();
unsigned long ulFind = m_ThreadMap.Find(tItem);
if( ulFind == - 1)
{
m_ThreadMap.Append(tItem);
return m_ThreadMap[m_ThreadMap.Find(tItem)].Second();
}
else
{
return m_ThreadMap[ulFind].Second();
}
}
public:
CThreadedRTAbsFactory()
{
}
virtual ~CThreadedRTAbsFactory()
{
}
template<class Tsubs, class Torg>
mxPGUID Override(Tsubs*, Torg*)
{
GUIDMap& IDMap = GetMap();
mxPGUID PrevClassID;
mxPGUID BaseClassID = Torg::ClassID();
mxPGUID SubClassID = Tsubs::ClassID();
TGUIDItem item(BaseClassID, 0);
TGUIDItem& Oitem = IDMap[IDMap.Find(item)];
PrevClassID = Oitem.Second();
Oitem.Second() = SubClassID;
return PrevClassID;
}
template<class Torg>
void Override( mxPGUID ClassID, Torg* )
{
GUIDMap& IDMap = GetMap();
mxPGUID BaseClassID = Torg::ClassID();
TGUIDItem item(BaseClassID, 0);
TGUIDItem& Oitem = IDMap[IDMap.Find(item)];
Oitem.Second() = ClassID;
}
template<class Tsubs, class Torg>
void Configure(Tsubs*, Torg*)
{
GUIDMap& IDMap = GetMap();
mxPGUID BaseClassID = Torg::ClassID();
mxPGUID SubClassID = Tsubs::ClassID();
TGUIDItem item(BaseClassID, SubClassID);
IDMap.Append(item);
}
void Configure( TGUIDItem item )
{
GUIDMap& IDMap = GetMap();
IDMap[IDMap.Find(item)].Second() = item.Second();
}
template<class Torg>
TGUIDItem GetConfig(Torg*)
{
GUIDMap& IDMap = GetMap();
mxPGUID BaseClassID = Torg::ClassID();
TGUIDItem item(BaseClassID, 0);
return IDMap[IDMap.Find(item)];
}
template<class T>
void Instance(T*& pT)
{
GUIDMap& IDMap = GetMap();
CTSingletonPtr<CClassReg> pReg;
mxPGUID BaseClassID = T::ClassID();
TGUIDItem item(BaseClassID, 0);
mxPGUID SubClassID = IDMap[IDMap.Find(item)].Second();
pT = reinterpret_cast<T*>(
pReg->GetFactory(SubClassID)->Instance() );
}
};
Now that we can configure a factory per thread, we need a Flyer factory template:
template<class T>
class CTFlyerRegEntry : public CTClassRegEntry< T >
{
protected:
static T* m_pInstance;
public:
static inline bool Configure( T* pInstance )
{
if( pInstance != 0 )
{
pInstance->m_pPrevious = reinterpret_cast<T::_tBase*>(m_pInstance);
}
m_pInstance = pInstance;
return true;
}
static inline bool Unconfigure( T* pInstance )
{
if( pInstance != 0 )
{
m_pInstance = reinterpret_cast<T*>(pInstance->m_pPrevious);
return true;
}
return false;
}
virtual void* Instance()
{
return m_pInstance;
}
virtual void Release( void* )
{
}
};
There are two ways we can use this to create a class with Flyer instancing. The MFC style method with a couple of macros, or the ATL style method using a templated base class.
The MFC-ish method needs these macros:
#define DECLARE_CLASS_FLYER( _CLASS, _BASE )\
\
private:\
friend class CTFlyerRegEntry< _CLASS >;\
\
protected:\
typedef _BASE _tBase;\
_BASE* m_pPrevious;\
static CTFlyerRegEntry< _CLASS > RegEntry;\
public:\
DECLARE_OCLASS_ID( _CLASS );\
_CLASS()\
{\
RegEntry.Configure(this);\
}\
~_CLASS()\
{\
RegEntry.Unconfigure(this);\
}
#define IMPLEMENT_FLYER( _CLASS, _BASE )\
IMPLEMENT_OCLASS_LUID( _CLASS );\
CTFlyerRegEntry< _CLASS > _CLASS::RegEntry;\
_CLASS* CTFlyerRegEntry< _CLASS >::m_pInstance = 0
A class that uses these might look like this:
class CDemoFlyer
{
public:
DECLARE_CLASS_FLYER( CDemoFlyer, CDemoFlyerBase );
void SetValue( double dValue )
{
m_dValue = dValue;
}
double GetValue( void )
{
return m_dValue;
}
private:
double m_dValue;
};
IMPLEMENT_FLYER( CDemoFlyer, CDemoFlyerBase );
If the macro magic is just too much for you, or it happens to be more to your taste, you might prefer an ATL-ish implementation. This requires an intermediate base class CTFlyer<T>
.
template< class T, class Base >
class CTFlyer
{
protected:
friend class CTFlyerRegEntry< T >;
typedef Base _tBase;
Base* m_pPrevious;
static CTFlyerRegEntry< T > RegEntry;
public:
CTFlyer()
{
RegEntry.Configure( (T*)this );
}
~CTFlyer()
{
RegEntry.Unconfigure( (T*)this );
}
};
Now, we just need to derive a class from CTFlyer
and give it an ID to make it a Flyer. What could be easier?
class CDemoFlyer : public CTFlyer< CDemoFlyer, CDemoFlyerBase >
{
public:
DECLARE_OCLASS_ID( CDemoFlyer );
void SetValue( double dValue )
{
m_dValue = dValue;
}
double GetValue( void )
{
return m_dValue;
}
private:
double m_dValue;
};
IMPLEMENT_CLASS_FLYER( CDemoFlyer, CDemoFlyerBase );
The demo code is a VC8 Console App solution which is indented to be run in Debug within the Visual Studio IDE. It provides a very simple functional demo of each of the creational patterns. It is designed to be as self contained as possible, implementing its own data structures. For most, this probably isn't the way you'd want to use the significant classes, but it does make it clear exactly what's required to make each class work.
Beating these templates into shape for publication has been interesting. It has made me refer back to the Patterns book and think of other ways I might make use of some of these classes to improve my code. Flyer, in particular, needed a surprising amount of work. It made much more sense in my head than on the screen. I hope I've changed that. Its usefulness may not at first be obvious. Just imagine a Flyer as something that gets passed with every function call made while it exists, but at no cost unless you actually use it. The downside is that when you do use it, it's not a particularly cheap call. In my opinion, this makes Flyer the perfect vehicle for an error handling mechanism. In my next article, I hope to demonstrate just that with a comprehensive Win32 error handling framework. As always, comments and suggestions are welcome. There is, I'm sure, much that could be improved.
This article was constructed with the assistance of:
- 14th July 2007: Original article.