Click here to Skip to main content
Click here to Skip to main content

Dynamic Class Factory

, 24 Jul 2004
Rate this:
Please Sign up or sign in to vote.
An article about class factory with dynamic subscription.

Introduction

This is the article about possible implementation of class factory, which uses dynamic subscription. It allows creation of objects based on custom criterion, useful, for example, in parsing of XML or other type of data, or for creating objects loaded from database.

Background

Has it happened to you that you had to write a huge switch command doing nothing but creating new objects? And include all the headers for all the objects into one CPP file? And then, when you did a small change in one of the headers, it took ages to compile?

Something similar to this:

CBase* pBase(NULL);
switch (type_variable)
{
    case obj1: pBase = new CBaseDerivate1();break;
    case obj2: pBase = new CBaseDerivate2();break;

    ...

    case objN: pBase = new CBaseDerivateN();break;
}

or worse:

CBase* pBase(NULL);

if (!strcmp(string_for_type, "Type1")) pBase = new CBaseDerivate1();
else if (!strcmp(string_for_type, "Type2")) pBase = new CBaseDerivate2();
    ...
else if (!strcmp(string_for_type, "TypeN")) pBase = new CBaseDerivateN();

P.S.: This could be even worse when complex conditions are used.

Tackling the problem

When I was implementing an XML parser and loading stored objects from DB, I got a lot of similar constructions. Well, I got tired from it quite soon. After becoming more experienced, I started to think about it. And recently, when I needed to use a similar code again, I found out the following approach...

What I actually want is a 'class factory', something that would create classes for me.

CBase* pBase = CClassFactory::CreateInstance(type_variable);

I could hide object construction into a separate class factory. In ATL, class factory uses a static array of available classes with pointer into CreateInstance function. New entries are added by the wizard. But the problem with headers and compilation (still lots of the includes would go into CPP) remains here.

Then, I came up with an idea - what about static array/map? But populated dynamically? And all classes would 'register' their creator function themselves. Would it be possible? How?

The idea of static array/map populated dynamically sounded very strange at the beginning, but what about declaring std::map/vector as static? And populate it by some clever mechanism later... Yep, we can do it.

Now, for the self registration bit. It took me some moments to come up with the solution, but it was simple (once found). We can sneak some code into initialization of static dummy variable... Like:

int CMyClass::iDummy = 
    ClassFactory::RegisterCreatorFunction(key, CMyClass::Creator)

where function RegisterCreatorFunction will return any integer and add creator function pointer (must be static too) into array/map.

OK, you might say... This seems quite easy to do.

So, now we have:

static std::map<key, pointer_to_create_function> mapOfCreators;

class CBaseDerivate1: public CBase
{
    static CBase* create_function();
    static key DummyVariable;
}

in header and

key CBaseDerivate1::DummyVariable = RegisterCreatorFunction(key, 
       CBaseDerivate1::create_function)

in CPP file.

If you try it, there is 50% chance that it will work. Yes, there is a catch: it might happen that static dummy will be initialized before the initialization of static array/map. And this whole code will throw an unhandled exception. But there is a cure for it - we won't access static array directly but by static get function, so the array/map will be created at the first access...

So instead of:

static std::map<key, pointer_to_create_function> mapOfCreators;

we need a 'get' function:

static std::map<key, pointer_to_create_function> * get_mapOfCreators()
{
    static std::map<key, pointer_to_create_function> _mapOfCreators;
    return &_mapOfCreators;
}

Solution

And when we encapsulate everything into class, we can get something like:

template <typename _Key, typename _Base, 
  typename _Predicator = std::less<_Key> >
class CClassFactory
{
public:
    CClassFactory() {};
    ~CClassFactory() {};

    typedef _Base* (*CreatorFunction) (void);
    typedef std::map<_Key, CreatorFunction, _Predicator> _mapFactory;

    // called at the beginning of execution to register creation functions
    // used later to create class instances
    static _Key RegisterCreatorFunction(_Key idKey, 
                              CreatorFunction classCreator)
    {
        get_mapFactory()->insert(std::pair<_Key, 
             CreatorFunction>(idKey, classCreator));
        return idKey;
    }

    // tries to create instance based on the key
    // using creator function (if provided)
    static _Base* CreateInstance(_Key idKey)
    {
        _mapFactory::iterator it = get_mapFactory()->find(idKey);
        if (it != get_mapFactory()->end())
        {
            if (it->second)
            {
                return it->second();
            }
        }
        return NULL;
    }

protected:
    // map where the construction info is stored
    // to prevent inserting into map before initialisation takes place
    // place it into static function as static member, 
    // so it will be initialised only once - at first call
    static _mapFactory * get_mapFactory()
    {
        static _mapFactory m_sMapFactory;
        return &m_sMapFactory;
    }
};

So, let's sum it up

Now, everything is prepared to be used. To do so, it is necessary:

  • Decide, what to use as a key (e.g., integer/string/GUID/pair of values) and define corresponding predicate functor.
  • In class definition, add static "creator" function and dummy variable:
    static CSampleBase* SampleCreatorFunction() {return new CSampleOne;}
    static int iDummyNr;
  • To initialize dummy variable, call the code to register creator function:
    int CSampleOne::iDummyNr = CClassFactory<int, 
        CSampleBase >::RegisterCreatorFunction(1, 
        CSampleOne::SampleCreatorFunction);
  • And to create an object, just call:
    CSampleBase * pBase = CClassFactory<int, 
         CSampleBase >::CreateInstance(1);

Known constraints:

This class will work perfectly if you don't start to play with multiple threads. It is well known(?) that MS <map> itself doesn't play nicely in multithreading environment, so that would be first place to watch... My solution was to protect all class calls by critical section, but it wasn't generic solution so I won't include it here.

Also, the way it is presented here allows only one instance of class factory per combination of Key/Base/Predicator. Which is OK for most instances, but there might be cases when it isn't - then a new template parameter will help.

License

This article has no explicit license attached to it but may contain usage terms in the article text or the download files themselves. If in doubt please contact the author via the discussion board below.

A list of licenses authors might use can be found here

Share

About the Author

mimicry
Web Developer
United Kingdom United Kingdom
No Biography provided

Comments and Discussions

 
GeneralTo get the code to compile on more recent versions of g++ Pinmemberrm_2517-Apr-09 4:49 
Questionregarding clean-up Pinmemberjayeshskul18-Nov-06 20:09 
AnswerRe: regarding clean-up Pinmembermimicry19-Nov-06 12:04 
QuestionRe: regarding clean-up Pinmemberjayeshskul23-Nov-06 6:16 
GeneralGood Job!! Pinmemberjjw626320-Oct-05 19:43 
GeneralBeware of using in static libraries Pinmember7-ZARK-726-Jul-04 1:05 
GeneralRe: Beware of using in static libraries Pinmembermimicry26-Jul-04 2:39 
GeneralRe: Beware of using in static libraries Pinmember7-ZARK-728-Jul-04 21:22 
GeneralRe: Beware of using in static libraries Pinmembershaochen198029-Jul-04 3:49 
GeneralRe: Beware of using in static libraries Pinmember7-ZARK-72-Aug-04 1:21 
GeneralRe: Beware of using in static libraries Pinmemberjhuang@televigation.cn22-Nov-04 22:59 
GeneralAnd very similar to one of my examples PinmemberPatje25-Jul-04 21:03 
In http://www.codeproject.com/cpp/all_kinds_of_factories.asp[^] I have given an overview of some factory alternatives. One of the alternatives is similar to what you write (see factories 4 and 6).
Maybe you can take a look at the other alternatives.
 
Patje

 
Enjoy life, this is not a rehearsal !!!


GeneralThis seems similar to the &quot;Abstract Factory&quot; design pattern from GoF. PinmemberDon Clugston25-Jul-04 13:58 
GeneralRe: This seems similar to the &quot;Abstract Factory&quot; design pattern from GoF. Pinmembermimicry25-Jul-04 23:41 
GeneralRe: This seems similar to the &quot;Abstract Factory&quot; design pattern from GoF. PinmemberJohn M. Drescher26-Jul-04 11:48 

General General    News News    Suggestion Suggestion    Question Question    Bug Bug    Answer Answer    Joke Joke    Rant Rant    Admin Admin   

Use Ctrl+Left/Right to switch messages, Ctrl+Up/Down to switch threads, Ctrl+Shift+Left/Right to switch pages.

| Advertise | Privacy | Mobile
Web04 | 2.8.140814.1 | Last Updated 25 Jul 2004
Article Copyright 2004 by mimicry
Everything else Copyright © CodeProject, 1999-2014
Terms of Service
Layout: fixed | fluid