Click here to Skip to main content
Click here to Skip to main content
Go to top

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

, 9 Jun 2010
Rate this:
Please Sign up or sign in to vote.
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:

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:

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:

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

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

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

#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:

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

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:

typedef   RegDerivedClasses<typelist_4(derived0> RegClassStruct;

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

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:

#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)

Share

About the Author

geoyar
Software Developer
United States United States
No Biography provided

Comments and Discussions

 
GeneralInteresting use of metaprogramming... PinmemberAescleal9-Jun-10 8:13 
GeneralRe: Interesting use of metaprogramming... Pinmembergeoyar11-Jun-10 15:57 
GeneralRe: Interesting use of metaprogramming... PinmemberAescleal11-Jun-10 19:58 
if you use singletons you introduce problems with multithreading and you can't unit test your code. Essentially singleton is an anti-pattern that acts like a fig leaf for using global state.
 
Cheers,
 
Ash
GeneralRe: Interesting use of metaprogramming... Pinmember aphazel 死神12-Jun-10 4:34 
GeneralRe: Interesting use of metaprogramming... PinmemberAescleal12-Jun-10 7:58 
GeneralRe: Interesting use of metaprogramming... Pinmembergeoyar12-Jun-10 12:08 
GeneralRe: Interesting use of metaprogramming... PinmemberAescleal12-Jun-10 23:43 

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
Web03 | 2.8.140916.1 | Last Updated 9 Jun 2010
Article Copyright 2010 by geoyar
Everything else Copyright © CodeProject, 1999-2014
Terms of Service
Layout: fixed | fluid