Click here to Skip to main content
65,938 articles
CodeProject is changing. Read more.
Articles / desktop / Win32

Object Factory Design Pattern: Register Creator Functions Using TypeLists and Template Metaprogramming

4.33/5 (3 votes)
9 Jun 2010CPOL4 min read 50.9K   627  
How to register classes for Object Factory using TypeList and Template Metaprogramming

Introduction

In my project, I am using the Object Factory Design Pattern [1]. I wanted to give a user an option to bring his own class to the Object Factory. It is simple: you define your class, register it with the factory, and that is it. I wanted the user to make minimum changes to code, and definitely not play with some code deep inside an application from where the registration function is called. There are results.

Classes

The Object Factory creates objects at run time in response to actions of a user (or flow of information.) Type of the object to create is unknown at compile time. The object factory needs object ID only to create an object, because it has object classes registered at compile time.

The object creation uses polymorphism, so the object classes should be derived from some abstract class, like:

C++
class CBase
{
public:
  CBase(){}
  virtual ~CBase(){}
 
....................................
};

Derived classes must be derived from the base class. Because the object registration with the factory should be done before object creation, the derived classes must have two static functions: the creator function and the function to register the derived class with the object factory, and the unique static type identifiers:

C++
class CDerived:public CBase
     {
     public:
static const int m_id;
     public:
           CDerived(void) {}
           virtual ~CDerived(void){}
 
          static CBase* CreateDerved() {return new CDerived;}
          static void RegisterDerivedClass(void);
     ................................................
};

There CreateDerived is a Creator function.

The type identifiers might be any objects: integers, strings, etc. For simplicity, I use integers.

Object Factory Class

The object factory keeps pairs Type Identifier - Creator Functions in some container that is a member of the factory class. A process of initialization of the container is called the class (type) registration. It seems that the most convenient container is a std::map.

To provide encapsulation, the factory has to have member functions to register and unregister classes.

More often than not, the factory class is implemented as a Singleton:

C++
class CFactory  // Scott Meyer's Singleton
{
public:
  typedef CBase* (*DerivedClassCreatorFn)(void);
private: // Singleton stuff
  CFactory() {}
  CFactory(const CFactory&);
  CFactory operator =(const CFactory&);
public:
  ~CFactory() {}
 
public: 
  static CFactory& Instance(void)
  {
    static CFactory factory;
    return factory;
  }
 
  void RegisterClassCreator(int id, DerivedClassCreatorFn 
                                    creatorFn)     
  {
    m_mapClassCreators.insert(std::map<int, />::value_type(id, creatorFn));
  }
 
  void UnregisterClassCreator(int id)
  {
    m_mapClassCreators.erase(id);
  }
 
private:
  std::map<int, /> m_mapClassCreators;
};

I have omitted error handling in RegisterClassCreator() and UnregisterClassCreator().

The RegisterDerivedClass function in the Derived class simply calls RegisterClassCreator function with appropriate type id and pointer to class Creator Function.

So somewhere in an application registration is implemented in a loop like: for all classes DerivedClass::RegisterDerivedClass().

You have to provide fully classified names to call the static Creator Functions. How to automatically get the type names for this loop? It is where Type Lists and template metaprogramming can help.

Type List

Loki free library has definition and code for type lists. I do not want to make you download and install Loki because we need only few definitions and defines.

This is straight from [2].

Definition of TypeList

C++
template <class>
struct TypeList
{
  typedef T Head;
  typedef U Tail;
};
 
     Null Type:
class NullType
{};

To make code less verbose, [2] introduced defines:

C++
#define TYPELIST_1(T1) TypeList<t1>
#define TYPELIST_2(T1, T2) TypeList<t1>
#define TYPELIST_3(T1, T2, T3) TypeList<t1>
#define TYPELIST_4(T1, T2, T3, T4) TypeList<t1>
...........................................................

We will use a TypeList in MetaLoop to get class types.

How It Works

Suppose we have four classes to registers: CDerived0, CDerived1, CDerived2, and CDerived2 with their type identifiers.

Let's define MetaLoop:

C++
template <class>
struct RegDerivedClasses;
 
Partial template specialization to stop loop unwinding:
 
template <class>
struct RegDerivedClasses<typelist<head>, 0>
{
  typedef Head Result;
  static inline void Fn(void)
  {
    Result::RegisterDerivedClass();
  }
};

Partial Specialization to Unwind the Loop

C++
template <class>
struct RegDerivedClasses<typelist<head>, idx>
{
  typedef typename RegDerivedClasses<tail>::Result 
                                                   Result;
  static inline void Fn(void)
  {
    Result::RegisterDerivedClass();
    ReDerivedClasses<typelist<head>, idx - 1>::Fn();
  }
};

It seems like the keyword 'inline' here is crucial: it instructs a compiler to inject code at the place of invocation.

Finally, let's define alias:

C++
typedef   RegDerivedClasses<typelist_4(derived0> RegClassStruct;

Now all you have to do to register the classes is to write in application:

C++
RecClassStruct::Fn();

It is recommended to keep all code related to the Type List and struct RegDerivedClasses in the same file that keeps code for the base and derived classes and the factory class.

Bring Your Own Class

Suppose you want to add your own class, CDerived4, to the factory. Now you have five classes to register, four old classes and one new class.

Assuming you have access to the source file where CDerived are defined, you proceed in steps:

Write new class, CDerived4 with mandatory members CreateDerived() and RegisterDerivedClass() and its unique identifier. If there is no #define for next TYPELIST, write a new:

C++
#define TYPELIST_5(T1, T2, T3, T4, T5)
                   TypeList<t1>
Change last typedef; 
     typedef   RegDerivedClasses<typelist_5(derived0> RegClassStruct;

Compile and run.

Of course, there might be another modification of executable code to utilize the newly added class, but it is a different matter.

Demo Application

The source code is pretty much explained above.

The demo application supplied with this article is compiled with Microsoft VC++ 2010 Express. (You can download VC++ 2010 Express from the Microsoft web site for free.)

The application registers four classes, CDerived0 to CDerived_3 with the factory and uses the factory to create class instances and writes class names onto console.

I also included folder Doc with HTML documentation files generated by Doxygen.

Open the file 'index.html' in your browser or any HTML reader and navigate to any place you want.

Literature

  1. Eric Gamma and others., "Design Patterns: Elements of Reusable Object-Oriented Software", Addison-Wesley
  2. Andrei Alexandrescu, "Modern C++ Design: Generic Programming and Design Patterns Applied", Addison-Wesley

History

  • 8th June, 2010: Initial version

License

This article, along with any associated source code and files, is licensed under The Code Project Open License (CPOL)