Subscribing Template Classes with Object Factories in C++
by Guy Peleg
September 14, 2007

Summary
Object factories provide a useful abstraction for object construction. A special problem with object factories must be considered, however, when subscribing template classes with object factories. This article presents an overview of the "subscription problem" along with several solutions.
C++ Coding Standards

Object Factories are a useful abstraction of the object construction process. First you register, or subscribe, with a factory each class needing automated construction capability. A unique id is associated with each such class; you simply supply that id to the factory to get fresh object on demand. The instance is returned via a pointer to a base class with which you can work.

This article begins with a short review of the Object Factory idiom, and then demonstrates the "subscription problem," an issue that occurs when subscribing template classes with object factories. Finally, a small framework using template meta-programming is presented that deals with the problem.

Introduction

In this article I will use a simplified version of Andrei Alexandrescu's Object Factory as described in his book, Modern C++ Design. The complete class and other useful idioms can be found also in Loki1. (Note that in this article the Loki namespace identifier is omitted.)

Our working definition of the object factory looks like this:

template <typename AbstractProduct,
          typename IdentifierType,
          typename ProductCreator = AbstractProduct * (*)()>
class ObjectFactory
{
    typedef ObjectFactory<AbstractProduct, IdentifierType, ProductCreator> ThisClass;
public:
    AbstractProduct * create(const IdentifierType & id)
    {
        typename Associations::const_iterator i = this->associations_.find(id);
        if (this->associations_.end() != i)
        {
            return (i->second)();
        }
        return 0;
    }

    bool subscribe(const IdentifierType & id, ProductCreator creator)
    {
        return this->associations_.insert(Associations::value_type(id,
                                                                   creator)).second;
    }
    
private:
    typedef std::map<IdentifierType, ProductCreator> Associations;
    Associations associations_;
};

By default, ProductCreator is a function type that returns a pointer to AbstractProduct. A typical creator function might look like the following.

template <typename Base, typename Derived>
Base * createInstance()
{
    return new Derived;
}

Somewhere in your code you will have subscription statements which look like:

// SomeClass::ID() will return a unique ID for SomeClass
factory->subscribe(SomeClass::ID(), createInstance<ISomeBase, SomeClass>);

Nice and easy. But it seems that the subscription issue is usually overlooked. The reason is that subscribing "regular" classes is easy. The problems begin when you need to subscribe template classes that use policies.

Many times users of those classes know at compile time the kind of policies they need and so the Object Factories need only to be able to create those template classes with the specific policies needed. But there are times where the exact combinations of policies are only known at runtime (e.g. highly configurable applications where users are able to choose the policies). In those cases the Object Factories should be able to create the template class with all possible combination of policies (except the ones that can't/shouldn't work together).

With many template classes and many policies this might be a problem to write and maintain.

The Subscription Problem

Suppose, for example, that you're developing a game with a factory for Radar Stations:

typedef ObjectFactory<IRadar, std::string> RadarFactory;

The following statement subscribes a particular Radar class with the factory.

factory->subscribe(Radar::ID(),createInstance<IRadar, Radar>);

But what happens if the Radar is a template class which has some policies? For example, a Radar might have a HidePolicy with some implementations, e.g. it can not be hidden (NoHide), hidden for periods of time (TimeoutHiding) or some other complicated hidden policy (CHP). Radar objects might also have a DefensePolicy: NoDefense, BulletProofed and ShellProofed, so the Radar declaration might look like this:

template <typename HidePolicy, typename DefensePolicy>
class Radar : public IRadar
{...};

Suppose also that the user of the factory doesn't know at compile time the exact policies that will be needed. For example, a player might be eligible for any combination of policies based on the resources she earned during the game.

This change is very dramatic as far as the subscription is concerned. Now we need to subscribe the Radar with all possible combinations of its policies. We end up writing 9 statements. Here is one:

factory->subscribe(Radar<NoHide, NoDefense>::ID(),
                   createInstance<IRadar, Radar<NoHide, NoDefense> >);

"Yes", you might say, "subscription becomes a tedious job, but this is done only once, so it is not too bad". But this is not always the situation. During development and maintenance things tend to change. For example what happens if you need to add a new policy implementation (e.g. AtomicProofed)? In this case you need to remember to add three subscriptions (one for each HidePolicy). It gets even worse if Radar is now supposed to have a new type of policy such as RadiationPolicy that has also three implementations. Now, you have to rewrite the subscriptions and end up with 36 statements!

Large applications that might have several factories and template classes that need to be subscribed to might easily end up with hundreds of statements. For example, imagine you have a template class with four policies, where each policy has four implementations. In this case only you'll have to write 256 subscription statements.

Using Interfaces

The obvious thing to do here is to change the template policies into class hierarchies with interfaces as their base classes. For example:

class IDefense {....};
class BulletProofed: public IDefense{....};

This means that we will need to add DefenseFactory and to subscribe the concrete Defense classes.

When a new policy implementation is introduced, all we have to do is add a new subscription for the right factory. In the worst case scenario, when new type of policy is introduced, we will need to add a new factory and write the new subscriptions statements. The down side of this solution is that you have many more factories and you have to add some code to coordinate the creation of the class with its policies (and also deal with the id issue mentioned earlier).

This might be a good solution if interfaces are really needed, but using interfaces instead of templates to sidestep the subscription problem seems a bit awkward. After all, what if we really do need template classes? For example, many of the methods in the policies can be inlined while Interfaces will make those methods virtual which will end-up in performance hit.

Is there a way to use templates and still have an elegant way to subscribe them with factories?

Factory Subscribers

There is a way to get template metaprogramming to do the dirty work for us. Face it-the compiler is faster, more accurate and doesn't need a day off after writing 256 subscription statements. We will describe a family of "Factory Subscribers" to do the job. This article will demonstrate one member of the family, FactorySubscriber_2, which we will use for the Radar example.

First, here is FactorySubscriber_2 definition:

template <template <typename A1, typename A2> class C,
          typename   Policies_1,
          typename   Policies_2,
          typename   IPC,
          typename   Factory,
          typename   CMW>
struct FactorySubscriber_2;

The "2" indicates that this factory subscriber knows how to subscribe a class that has two template arguments.

FactorySubscriber_2 declares:

  1. C - The subscribed class as a template template parameter.
  2. Policies_1 and Policies_2 that stand for the two lists of policies (NoHide etc.).
  3. IPC - Represents all the Illegal Policies Combinations.
  4. Factory - The factory to which the classes will be subscribed to.
  5. CMW - "Creator Method Wrapper". We need to pass the creator method to the factory. But since we're dealing here with a template class and many policies that produce several distinct types, we can't use the createInstance that we saw before. Instead we will use a wrapper that will be able to fetch the creation method for every type. The nice thing here is that the wrapper only needs to appear in the base class. The nice thing here is that by using the wrapper class we don't need to know about those distinct types, rather we only pass the Base class (e.g. IRadar) to that wrapper.

Here is an example of a possible CMW for the classic new/delete creator:

        template <typename Base>
        struct NewCreatorWrapper
        {
                // Creates an instance of Derived
                // and returns that instance as pointer to its Base
                template <typename Derived>
                static Base * createMethod()
                {
                        return new Derived;
                }
        };

The FactorySubscriber_2 needs to work with two unknown lists of policies. This is easily achievable with Typelists.2

Now we need to specialize the FactorySubscriber_2 for the various scenarios it should support. Here's the first one:

// Specialized version which is for the case where we have two Typelists
template <template <typename A1, typename A2 > class C,
          typename   H1,
          typename   T1,
          typename   H2,
          typename   T2,
          typename   IPC,
          typename   Factory,
          typename   CMW>
struct FactorySubscriber_2<C, Typelist<H1,T1>, Typelist<H2,T2,> IPC, Factory, CMW>
{
  static bool subscribe(Factory * f)
  {
     return
        FactorySubscriber_2<C, H1, H2, IPC, Factory, CMW:>:subscribe(f) &&
        FactorySubscriber_2<C, H1, T2, IPC, Factory, CMW:>:subscribe(f) &&
        FactorySubscriber_2<C, T1, H2, IPC, Factory, CMW:>:subscribe(f) &&
        FactorySubscriber_2<C, T1, T2, IPC, Factory, CMW:>:subscribe(f);
  }
};

This specialized version applies when the FactorySubscriber_2 receives two Typelists; each list has at least one item (not including the NullType that is placed on every Typelist's tail).

This FactorySubscriber_2 has a static subscribe method that will subscribe all the possible combinations:

  1. Subscribe the head of Typelist 1 with the head of Typelist 2.
  2. Subscribe the head of Typelist 1 with the rest ("tail") of Typelist 2
  3. Subscribe the tail of Typelist 1 with the head of Typelist 2
  4. Subscribe the tail of Typelist 1 with the tail of Typelist 2

The second specialization of FactorySubscriber_2 is for the case where the first group of policies contains exactly one policy while the second group contains more than one policy.

template <template <typename A1, typename A2 > class C,
          typename   P,
          typename   H1,
          typename   T1,
          typename   Factory,
          typename   IPC,
          typename   CMW>
struct FactorySubscriber_2<C, P, Typelist<H1, T1>, IPC, Factory, CMW>
{
  static bool subscribe(Factory * f)
  {
     return
       FactorySubscriber_2<C, P, H1, IPC, Factory, CMW:>:subscribe(f) &&
       FactorySubscriber_2<C, P, T1, IPC, Factory, CMW:>:subscribe(f);
  }
};

In this specialization, the FactorySubscriber_2 will: 1. Subscribe the only policy (P) with the head of Typelist 2 2. Subscribe the only policy (P) with the tail of Typelist 2.

We have another specialized FactorySubscriber_2 that is very much the same as this one except that this time the first group of policies contains more than one policy while the second group has exactly one:

template <template <typename A1, typename A2 > class C,
          typename H1,
          typename T1,
          typename P,
          typename IPC,
          typename Factory,
          typename CMW>
struct FactorySubscriber_2<C, Typelist<H1, T1>, P, IPC, Factory, CMW>
{
  static bool subscribe(Factory * f)
  {
      return
       FactorySubscriber_2<C, H1, P, IPC, Factory, CMW:>:subscribe(f) &&
       FactorySubscriber_2<C, T1, P, IPC, Factory, CMW:>:subscribe(f);
  }
};

The next specialization is the stop condition. This version of FactorySubscriber_2 is the one that can subscribe the class in the factory. It assumes that it receives exactly one policy in each group:

template <template <typename A1, typename A2 >class C,
          typename P1,
          typename P2,
          typename IPC,
          typename Factory,
          typename CMW>
struct FactorySubscriber_2
{
private:
    // Typedefs to save typing
    typedef FactorySubscriber_2<C, P1, P2, IPC, Factory, CMW >ThisClass;
    typedef TYPELIST_2(P1, P2) PoliciesTL;

    // First, we find if at least one policy is a NullType
    enum { nullPolicies = IsNullType<P1>::value || IsNullType<P2>::value };

    // If   - nullPolicies == true then legalPolicies is set to false
    //        by using Int2Type<false>::value
    // Else - We check the policies against the Illegal Policies Combinations
    //        (IPC) and set its (IPCChecker<IPC, PoliciesTL>::value) to be
    //        the outcome of this check
    enum { legalPolicies = Select<nullPolicies,
                                  Int2Type<false,>
                                  IPCChecker<IPC, PoliciesTL >>::Result::value };

public:
    static bool subscribe(Factory * f)
    {
        // Activate the right subscribe method based on legalPolicies value
        return ThisClass::subscribe<C, 
                                    P1, 
                                    P2, 
                                    Factory,
                                    CMW>(f,
                                         Int2Type<legalPolicies>());
    }

private:
    // Overloaded template method for the case where legalPolicies is false
    template <template <typename A1_1, typename A2_1 > class C_1,
              typename P1_1,
              typename P2_1,
              typename Factory_1,
              typename CMW_1>
    static bool subscribe(Factory_1 * factory, Int2Type<false>)
    {
        return true;
    }


    // Overloaded template method for the case where legalPolicies is true
    template <template <typename A1_1, typename A2_1> class C_1,
              typename P1_1,
              typename P2_1,
              typename Factory_1,
              typename CMW_1>
    static bool subscribe(Factory_1 * factory, Int2Type<true>)
    {
        // Subscribe the class in the factory
        // The CMW assumes to have a 'createMethod' method
        return factory->subscribe(
                    C_1<P1_1, P2_1:>::ID(),
                    CMW_1::createMethod<C_1<P1_1, P2_1>> 
                                 );
    }
};

The subscribe method above uses another private subscribe method to subscribe the class C<P1, P2> to the factory. But before doing so, this class needs to make sure that the policies, P1 and P2, are legal.

There are two reasons why they might not be legal. First, typelists have a NullType type as their last element. This means that this specialized version might be instantiated with a NullType in one or two of its arguments which are useless for class 'C'. Second, even if neither policy is NullType, we still need to check them against the IPC. Only if the policies pass both checks do we subscribe the class C<P1, P2> to the factory.

The decisions are made by two enums. The important one, legalPolicies, depends on nullPolicies and so the compiler will first need to determine its value. nullPolicies will be true if at least one policy is a NullType. The IsNullType specialization does that job:

template <typename T>
struct IsNullType
{
    enum { value = false };
};

template <>
struct IsNullType<NullType>
{
    enum { value = true };
};

Next, the compiler will determine the value of legalPolicies. In case that nullPolicies is true than legalPolicies is false (value of Int2Type<false>). Otherwise the value is the outcome of applying IPCChecker (explained shortly) on both IPC and the two policies (wrapped as a Typelist).

Instantiating IPCChecker, however, is relevant only if nullPolicies is false. Loki's Select() function resolves that issue. Select has three arguments: a Boolean value and two types. The result is a type, either the first one (if the Boolean value is true) or the second one. This means that the compiler will need to evaluate only the value of the selected type (::Result::value). This idiom is important because it makes sure that the compiler doesn't evaluate both branches. For example, the following code seems harmless, but it's not.

enum
{
    legalPolicies = nullPolicies ? Int2Type<false>::value :
                                   IPCChecker<IPC, PoliciesTL >>::value
};

Although the value of nullPolicies is known to the compiler, it will still need to evaluate both values, which will increase compilation time, not to mention that there is no guarantee that both branches will compile. Once legalPolicies value is determined, it is used to call one of the private subscribe methods.

The private section of the class contains two overloaded of subscribe that have a very similar signature. The only difference is that one expects Int2Type<false> while the other receives Int2Type<true>. Loki's Int2Type transforms an integer or a Boolean value into a distinct type.

The first private subscribe method will be instantiated by the compiler if legalPolicies is false (either one or two of the policies are NullType or they form an illegal combination). This method is a no-op.

The second private subscribe method will be instantiated by the compiler if legalPolicies is true. In this case the class will be subscribed to the factory (at last!). Note that this method uses the CMW to obtain the pointer to the creator function for the class subscribed with the factory. Note that the compiler will compile only the method that is called.

Note also that we can't use an if statement to do the subscription, because the compiler would need to compile both branches of the if, which is problematic. For example the compiler will try to find an ID method for class C when P_1 is a NullType, so the following example is wrong:

    static bool subscribe(FACTORY * factory)
    {
        // This is wrong since the compiler will need to instantiate both
        // branches of the 'if' statement
        if (policiesAreLegal)
           return factory->subscribe(
                          C_1<P_1, P_2>::ID(),
                          CMW::createMethod<C<P_1, P_2> >()
                                 );
        else
            return true;
    }

Determining Whether Policies are Legal

The IPCChecker class checks policies for all factory subscribers:

template <typename IPC, typename CP>
struct IPCChecker;

It checks the policies (CP) against all illegal policies combinations (IPC).

There are some things that this framework assumes:

  1. Users that need to pass Illegal Policies Combinations will do it by Typelist of Typelists. This is because the number of combination is unknown as the number of policies in each combination (examples to follow).
  2. NullType represents an empty list which means that there are no restrictions.
  3. Illegal combinations can use the EmptyType3 as a wild card that symbolizes any type.
  4. The checked policies (CP) are passed as Typelist.

As before, we specialize the class to support all possible scenarios.

The first specialization (see below) is for the case where there is no illegal combination list, or for the case where we've reached the last element in IPC after checking CP against all the Typelists in IPC. Either way the result is true, which means that the combination is legal.

template <typename PC>
struct IPCChecker<NullType, PC>
{
    enum { value = true };
};

The second specialization (see below) relies on the fact a Typelist is itself a type. This applies when the first element of the IPC (which is a Typelist) matches (i.e., is the same type as) the combination of policies that we check. In this case the result is false, meaning that PC is an illegal combination.

template <typename CP,
          typename T>
struct IPCChecker<Typelist<CP, T>, CP>
{
    enum { value = false };
};

The next specialization below is for the case where the first element of the IPC (which is a Typelist) doesn't match (i.e., is not the same type as) the combination of policies that we check. In this case we still need to further check those Typelists because they might not be the same in terms of the type system, but they might be the same for the application. This might happen if the Typelist PC1 contains EmptyType that made PC1 a different type from PC2:

template <typename PC1,
          typename T,
          typename PC2>
struct IPCChecker<Typelist<PC1, T>, PC2>
{
    // Compare the first Typelist to the compared policies combination
    // If there is a match - this is an illegal policies combination
    // Else                - check the policies combination against the rest of
    //                       the Typelist
    typedef typename Select<IsSamePoliciesList<PC1, PC2>::value,
                            Int2Type<false>,
                            IPCChecker<T, PC2> >::Result Result;

    enum { value = Result::value };
};

This specialization uses IsSamePoliciesList (covered next) to check the unmatched Typelists (PC1 and PC2). If the returned value is true (there is a match) than we don't need to continue. Otherwise we need to check the policies against the rest (i.e. T) of the illegal policies combination list. Again, to save compilation time, we use here Loki::Select to make sure that the compiler instantiates one branch.

Now we are left with defining IsSamePoliciesList.

The declaration:

template <typename TL_1, typename TL_2>
struct IsSamePoliciesList;

This specialization deals with the case where we start by comparing two lists of policies. Here we check the result of comparing the two heads of the lists, if they match, than we continue to check the rest of the Typelists, otherwise the result is false, i.e., there is no match:

template <typename H1,
          typename T1,
          typename H2,
          typename T2>
struct IsSamePoliciesList<Typelist<H1, T1>, Typelist<H2, T2> >
{
    // Check the first type in each Typelist
    // If there is a match - continue and check the tail of each list
    // Else                - The outcome is false
    typedef typename Select<IsSamePoliciesList<H1, H2>::value,
                            IsSamePoliciesList<T1, T2>,
                            Int2Type<false> >::Result Result;

    enum { value = Result::value };
};

A few more specializations and we're almost done...

* A specialization for the case where we compare two Typelists which are the same. This specialization is needed to distinguish this case from the next one:

template <typename H,
          typename T>
struct IsSamePoliciesList<Typelist<H, T>, Typelist<H, T> >
{
    enum { value = true };
};

* A specialization for the case where the two compared policies are of the same type:

template <typename P>
struct IsSamePoliciesList<P, P>
{
    enum { value = true };
};

* A specialization for the case where the two policies are not of the same type:

template <typename P1, typename P2>
struct IsSamePoliciesList
{
    enum { value = false };
};

* A specialization for the case where we've reached the end of each Typelist:

template <>
struct IsSamePoliciesList<NullType, NullType>
{
    enum { value = true };
};

* Specializations for the case where one list is longer than the other

template <typename P>
struct IsSamePoliciesList<NullType, P>
{
    enum { value = false };
};
template <typename P>
struct IsSamePoliciesList<P, NullType>
{
    enum { value = false };
};

* A specialization for the case where a policy (P) is compared against an EmptyType (which symbolizes any type) and so the result is true:

template <typename P>
struct IsSamePoliciesList<EmptyType, P>
{
    enum { value = true };
};

Back to the factory

Although FactorySubscribers ease the subscription process, it would be more convenient and intuitive for users to interact with the factory rather than working directly with the FactorySubscribers. We can add some methods to the factory for that purpose:

    // A new method in the factory to subscribe template class with two
    // template arguments
    template <template <typename A1, typename A2> class C,
              typename Policies_1,
              typename Policies_2,
              typename IPC,
              typename CMW>
    bool subscribe()
    {
        return FactorySubscriber_2<C,
                                   Policies_1,
                                   Policies_2,
                                   IPC,
                                   ThisClass,
                                   CMW>::subscribe(this);
    }



    // A new method in the factory to subscribe template class with three
    // template arguments.
    // This method uses FactorySubscriber_3 which is not described in this
    // article).
    template <template <typename A1, typename A2> class C,
              typename Policies_1,
              typename Policies_2,
              typename Policies_3,
              typename IPC,
              typename CMW>
    bool subscribe()
    {
        return FactorySubscriber_3<C,
                                   Policies_1,
                                   Policies_2,
                                   Policies_3,
                                   IPC,
                                   ThisClass,
                                   CMW>::subscribe(this);
    }

Using the Factory Subscribers

Now let's use this framework to subscribe the Radar along with its two lists of policies. This statement will subscribe all 9 combinations.

factory->subscribe<Radar,
                   TYPELIST_3(NoHide, TimeoutHidding, CHP),
                   TYPELIST_3(NoDefense, BulletProofed, ShellProofed),
                   NullType, // No restrictions
                   NewCreatorWrapper<IRadar> >();

Some macros will clear things up even more:

// No restrictions
#define RADAR_IPC NullType 

#define RADAR_HIDE_POLICIES    TYPELIST_3(NoHide, TimeoutHidding, CHP) 
#define RADAR_DEFENSE_POLICIES TYPELIST_3(NoDefense, BulletProofed,\
                                          ShellProofed) 


factory->subscribe<Radar,
                   RADAR_HIDE_POLICIES,
                   RADAR_DEFENSE_POLICIES,
                   RADAR_IPC,
                   NewCreatorWrapper<IRadar> >();

Adding a new AtomicProofed policy implementation will be done like this:

#define RADAR_DEFENSE_POLICIES TYPELIST_4(NoDefense, BulletProofed,\
                                          ShellProofed, AtomicProofed) 

TYPELIST_3 was changed into TYPELIST_4 and AtomicProofed was added to that group. That's it. The subscription statement stays the same.

Adding a new type of Policies (Radiation) is easy too. We add a new macro:

#define RADAR_RADIATION_POLICIES TYPELIST_3(NoRadiation, LightRadiation, \
                                            DangerousRadiation)

.... and change the subscription statement:

// This subscribe method will use the FactorySubscriber_3. 
factory->subscribe<Radar,
                   RADAR_HIDE_POLICIES,
                   RADAR_DEFENSE_POLICIES,
                   RADAR_RADIATION_POLICIES,
                   RADAR_IPC,
                   NewCreatorWrapper<IRadar> >();

Restricting combinations of policies can now be as easy as changing a macro:

#define RADAR_IPC TYPELIST_1(TYPELIST_3(NoHide, ShellProofed,\
                                        DangerousRadiation))

Here we declare that we have one illegal combination. Note that we need to use a Typelist of Typelists. Another option:

#define RADAR_IPC TYPELIST_2(TYPELIST_3(NoHide, ShellProofed,    \
                                        DangerousRadiation),     \
                             TYPELIST_3(EmptyType,AtomicProofed,\
                                        DangerousRadiation))

In this case we have two illegal combinations; the second one means that AtomicProofed can't work together with DangerousRadiation no matter what the hidden policy is (Loki's EmptyType is used here as a wild card to symbolize any type). Note also that there is no impact on the subscription statement.

Conclusion

The framework described in this article lets you easily write and change subscription statements of template classes in factories. The downside is that many classes are produced along the way-classes that don't do anything. But on the other hand most of these classes are likely to be optimized away by the compiler.

Writing the code for more complicated subscribers (e.g. FactorySubscriber_3, FactorySubscriber_4) is not trivial. But you can comfort yourself that once you've done writing that code you've gained a lot: You have a framework that can work with any factory and any class and its lists of policies, and you have all the code that does the subscription written and maintained in one place. Using this framework makes subscription code elegant, easy

Acknowledgments

Thanks to all the reviewers for their input. Thanks to Chuck Allison, for editing this article. Thanks also to Andrei Alexandrescu for his comments and encouragement.

References

[1] http://loki-lib.sourceforge.net

[2] Andrei Alexandrescu - Modern C++ Design

[3] As its name implies, it is just an empty class. It can be found in Loki.

Talk back!

Have an opinion? Readers have already posted 6 comments about this article. Why not add yours?

About the author

-