MFC gives you a beautiful framework to dynamically create classes as long as you derive them from CObject. In a project much like the ColorFilter demo I'm using as an example here, I was developing, the user had to be able to select one out of several derived class. As the project evolved the number of these derived (leaf) classes increased and I found that I had to make changes to the program at various points and inevitably always forgot to make one of them. Using CObject and MFC's runtime class information seemed at first a possible solution but unfortunately this framework does not let you differentiate between leaf classes derived from different (virtual) base classes as CObject is the base class for all of them. Moreover you must include MFC in your project. I have therefore developed a set of macro's and a templated factory class that allows the most derived class (i.e. the leaf) to register itself with the base class from which it is derived. All leaf classes can thus be enumerated and created through the base class.
The demonstration project shows one (very simplistic) way in which enumeration of leaf classes can be used. A number of color filters are derived from a base filter class. Each leaf class is automatically added to the menu bar where the user can select it. Upon selection of a particular color filter, the filter is created and applied to a circular section of the displayed bitmap.
The real benefit from this enumeration capability lies in the fact that hardcoded knowledge of each leaf class becomes unnecessary. If you want to add another filter you don't need to add another menu handler or update a switch statement.
Virtual base classes are regularly used as abstract interfaces. A pointer to the base class is sufficient to call functions in the leaf class.
class CLeaf : public CBase
CBase* pBase = new CLeaf;
As this little sample shows, hardcoded knowledge of derived classes is necessary (
). If you derived a dozen classes from the same base class and you wanted to instantiate one out of twelve, you're first approach would probably be code like this:
CBase* pBase = NULL;
case 0: pBase = new CLeaf_0; break;
case 1: pBase = new CLeaf_1; break;
case 2: pBase = new CLeaf_2; break;
Adding leaf class number 13 now becomes a real headache. You have to remember to update the switch statement somewhere in your code. Do you always remember how many you've implemented and in which files?. If you want to write solid code you should only have to make changes in one place.
Using the code
All the necessary code to implement enumerating leaf classes with factories is contained in one header file: BootStrap.h. Include this file in your project and add the macro calls to your base and leaf classes. The
IMPLEMENT_LEAF_CLASS() only need to be added to the most derived class.
Declaration of the base class in the demo application:
virtual COLORREF ChangeColor(...) = 0;
And the implementation of this base class:
COLORREF cr = ChangeColor(crOldPixelColor);
IMPLEMENT_ROOT_CLASS(CBaseFilter) macro adds two public, static functions to your base class. They are:
static int GetRegisteredManufactoringPlantCount();
static CBootStrapper<base_class>* GetRegisteredManufactoringPlant(int nIndex);
The first function lets you find out how many leaf classes are derived from the base class and with the second you obtain a pointer to the factory for each leaf class.
A very simple class derived from this base class is declared like this:
class CBlueFilter : public CBaseFilter
virtual COLORREF ChangeColor(COLORREF crPure);
and implemented like this:
IMPLEMENT_LEAF_CLASS(CBlueFilter, CBaseFilter, _T("Blue Filter"))
COLORREF CBlueFilter::ChangeColor(COLORREF crPure)
COLORREF crNew = RGB(0, 0, GetBValue(crPure));
As you can see there is very little work needed to add this functionality to a class structure. And because all the necessary changes to another flavor of leaf class can be kept local to one file and one section of code, there is very little danger in copy and pasting.
The first example shows how to use the added functionality to add the names of the leaf classes to a menu item in the mainframe's menu bar. This is done using the
GetClassName() function in the
BOOL CChildView::Create(LPCTSTR lpszClassName, ...)
CMenu* pMenu = pMain->GetMenu();
CMenu* pSubMenu = pMenu->GetSubMenu(1);
if(NULL != pSubMenu)
CBaseFilter* pBase = NULL;
int nLeaves = pBase->GetRegisteredManufactoringPlantCount();
UINT nMenuID = ID_FILTER_NONE + 1;
for(int nIdx = 0; nIdx < nLeaves; nIdx++, nMenuID++)
CBootStrapper<CBaseFilter>* pBoot =
pSubMenu->AppendMenu(MF_STRING | MF_ENABLED,
The second example shows how to create an instance of a leaf class using the
CreateObject() function in the
BOOL CChildView::OnCmdMsg(UINT nID, int nCode, void* pExtra,
UINT nFirstID = ID_FILTER_NONE;
UINT nLastID = nFirstID +
if( (nID < nFirstID) || (nID > nLastID))
return CWnd::OnCmdMsg(nID, nCode, pExtra, pHandlerInfo);
if(NULL == pHandlerInfo && CN_COMMAND == nCode)
if(nID == nFirstID)
m_dcFiltered.BitBlt(0, 0, m_bmp.bmWidth,
m_bmp.bmHeight, &m_dcSource, 0, 0, SRCCOPY);
int nFilterIndex = nID - nFirstID - 1;
CBootStrapper<CBaseFilter>* pBoot =
CBaseFilter* pFilter = pBoot->CreateObject();
pFilter->Apply(&m_bmp, &m_dcSource, &m_dcFiltered);
Points of Interest
Static variables are constructed before any user code is executed thus while the
IMPLEMENT_LEAF_CLASS() macro is relying on the
) having been executed there is no obvious way this can be enforced in user code. And according to the MSDN library the only guarantee you get is that static objects are constructed in the order in which they appear in the source file, but that no guarantee can be given as to the order in which the source files are linked! Therefore it would seem that all the static objects in the base class and its derived classes should be in the same source file so that their order would ensure proper initialization. A very annoying situation because that prevents the use of individual implementation files for base class and derived class.
At first I hoped to solve this problem by concentrating the implementation of static code in templated functions, the leaf class then only has to derive itself from the base class and the template class. A bug in the VC++ 6.0 linker prevents this because it can not find the code for a static function in a template class (Error: LNK2001). My second thought was to put some compiler directives in the macro functions warning if the
IMPLEMENT_LEAF_CLASS(xx) did not appear in the same file as the
IMPLEMENT_ROOT_CLASS(xx) macro. Well, compiler directives are not allowed inside macro functions (who'd have thunk! so many silly bugs could be prevented by a few simple macro's.).
Going through the MSDN database for a possible solution, I came across the
#pragma init_seg() compiler directive. With it you can force the compiler to put the constructors (and destructors of course) of your static objects in special initialization segments. Objects in the "compiler" segment are constructed before objects in the "lib" segment which are constructed before code in the "user" segment. By default, the code you write is placed in the "user" segment. Thus by placing the constructor of the factory vector in the base class in the "lib" segment, it is assured to have been initialized before the constructors of the factories in the leaf classes add themselves to this vector!
#pragma init_seg() works as advertised and I used it in the first version of the macro set. But as PeterH quite rightly pointed out, compiler directives are not portable. And besides all my efforts to keep everything in single statements, the need to add this statement and the absence of anything to force its presence or detect its absence still didn't make it foolproof. So based upon PeterH's comment I investigated the possibility to use the C++ standard to beat this problem. Now a little silly looking statement in the
IMPLEMENT_ROOT_CLASS(xx) macro forces the compiler to create the static vector in the base class the first time a leaf class registers its static factory class.
unsigned int base_class::RegisterManufactoringPlant(
static bool blCreate = (NULL ==
new std::vector<CBootStrapper<base_class>*>() ));
- Created around the end of the summer of '03
- Improved upon suggestions by PeterH in December '03
This member has not yet provided a Biography. Assume it's interesting and varied, and probably something to do with programming.