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

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 
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 
Two points I'd like to make here...
 
I have no objection to using a typelist as a method of defining a list of classes at compile time. If you have a list of classes that you want to define at compile time and have a common set of operations performed on them at runtime then typelist is a good way to do it. That bit's the interesting use of metaprogramming. So I don't have any problem with object factories or typelists. However making an object factory a global or singleton is the thing I really don't like about your implementation.
 
So why do I think making an object factory any sort of global facility is a mistake? It goes down to the root of what an object factory is - it's there to decouple the creation of an object from where it's used. As soon as you make the factory a global instance then you couple everything back together again.
 
You say that that you don't know of when you'll need multiple object factories in the same application. If you tried writing a set of unit tests for code that depended on the factory you'd end see fairly quickly that you'd need to be able to substitute different factories for different tests. Note that I'm not talking about testing your object factory itself but testing code that depends on the factory.
 
My third point (of two, isn't editing wonderful?) is that Alexandrescu didn't research how to write multithreaded singletons that well until after he published his book (he really only just templatised and used policies to customise the prevailing single threaded model). He's a bright bloke but he missed all the discussion going on in the Java world (the whole "double checked locking is broken" debate) that showed that a good chunk of his implementation was flawed. If someone like Andrei (who really popularised template metaprogramming with his book) can't get multithreaded singleton right then the rest of us don't really stand a chance.
 
Not sure if this count as point 4 or not, this is getting a bit stream of consciousness...
 
In Alexandrescu and Myer's article on how double checked locking is broken they suggest that the only efficient way of using a singleton in a mutithread environment is to cache a reference to the singleton at the start of each thread and pass it around as a parameter:
 
void thread_main( void * )
{
    singleton &s( singleton::instance() );
 
    ....
 
    use_singleton( s );
}
 
So here there's no reason for the object to be a singleton. The obvious extension here is to pass a pointer to whatever your singleton is into the thread as part of it's startup parameters:
 
void thread_main( void *context )
{
    was_singleton &s( *reinterpret_cast<was_singleton*>( context ) );
 
    ....
 
    uses_was_singleton( &s );
}
 
and then you don't have to worry about the object being a singleton, it's substitutable and acts like anything else.
 
So what's the point of all this? There's two (really this time, I counted in advance):
 
- Object factories shouldn't be global as you can't test dependent code
- There isn't an efficient multithreaded implementation of singleton in C++ that can beat using a local variable
 
Cheers,
 
Ash
 
PS: Sorry about the rambling nature - just shout about what's unclear and I'll try and amplify it.

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