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

Fun With Templates: Part 1

, 11 Jun 2003
Rate this:
Please Sign up or sign in to vote.
Explores some of the lesser known features of C++ templates.

Introduction

Like many of the features of C++, template classes are mostly used for the immediate task that they were designed for, giving programmers the ability to create re-usable classes which are abstracted from the type that they are designed to work on. It is a little known fact that templates can be applied to almost any token that appears in the class definition, which opens up some interesting possibilities.

This is also my first foray into the world of Code Project article writing, so any feedback would be most appreciated. No example code has been supplied, the reason for this being that the focus of this article is the design principles more than a specific piece of code.

Using templates to specify parent classes

Did you know you can use templates to specify what class your template class inherits from?

Consider the following piece of code:

template <class Parent> 
class MyClass : public Parent
{
public:
    void Foo();
};

Simple. By specifying Parent as a template parameter, you can now instantiate the class by specifying the parent class in the declaration as follows:

MyClass<CWnd> m_MyWndDerivedClass

m_MyWndDerivedClass is now an instantiation of MyClass, which in turn derives its behavior from CWnd. By changing the template parameter you can change what class MyClass inherits from on the fly. This means any behavior defined in MyClass can be applied to any class it inherits from. Kind of polymorphism in reverse, albeit not as powerful. Of course it is important to make sure that your class has member functions and variables that will not conflict with and foreseeable parent classes, so it would be a good idea to add an additional identifier to all variable/function names--such as MyClass_Init()

So what use is this? The possibilities are endless. But there is one especially good reason why this functionality is of great use, and that is when you begin to explore policy classes.

Policy Class Systems

Policy Class Systems are built upon the principal that, instead of defining concrete classes which have a predetermined set of behaviors, you actually design smaller "task related" classes named "Policies". Whenever there are multiple methods of performing a task, a policy class could be used to reflect the behavior. A compound class can then be constructed by the end-user of your library using any combination of the available policies. For example, a compound class could be composed of three main policies: A "Creation Policy", which handles how the object is created, where it is created, how memory was allocated, and so on and so forth; an "Initialization Policy", which could specify how the class is prepared for use, whether all the values are set to 0, whether the class should read data from a file, and so forth; the final policy could be a "Functionality Policy", which may specify whether the class should add a record to a database, send an e-mail to someone, or whatever. Truth be told, a policy based class would never have such a generalized policy as "what it actually does", but it will serve well as an example.

So how does one go about creating policies?

First off you need to decide what policies are relevant to the class. In the above example, I described three policies: "Creation", "Initialization" and "Functionality". A policy is declared as a standard templated class as follows:

template <class T> 
class CreationNew    
{
public:
    static T* Create()
    {
        return new T;
    }
};

By writing this class, we have enforced a common interface that must be shared between all creation policies, which is that they must all contain a Create method, taking no parameters and returning a pointer to a T object. You can derive from an abstract interface if you wish to further enforce this interface.

This is one of the possible creation policies used by our system. CreationNew is the most basic creation method, and simply allocates memory using the new operator, and returns a pointer. Here is another:

template <class T> 
class CreationPrototype {
public:
    static T* Create() { 

return new T(_item); };

protected:
    static T *_item;
};

This creation policy uses a prototype which is cloned whenever a new object of type T is requested.

The same method applies for creating the initialization policies, though their interface will differ in that they all contain a member function named Init as follows:

template <class T> class InitialisationZero 
                    { public: static void Init(T *pObj) { 
memset(pObj, 0, sizeof(T)); };
};

And finally the functionality policies, but as I said earlier having such a generalized policy for the functionality of your class isn't a good idea. After all you designed the class for a specific reason. Policy classes should relate to different methods of completing the same task, not different tasks altogether.

class FunctionalityHelloWorld { public: void DoSomething() { 
cout << "Hello World!"; };
};

Again, the common interface here is the DoSomething function, which should be present in all "Functionality Policies".

So now that we have designed all our policies, how do we create the compound class that will exploit them?

template 
<
    class CreationPolicy,
    class InitPolicy,
    class FunctionalityPolicy
> 
class CompoundClass : public CreationPolicy, 
   public InitPolicy, public FunctionalityPolicy 
{ 
};

There, simple, now the end-user can construct the compound class from the various policies, by using typedefs as follows:

typedef CompoundClass< CreationNew<MyClass>, 
   InitialisationZero<MyClass>, 
   FunctionalityHelloWorld > MyCompoundClass;
typedef CompoundClass< CreationPrototype<MyClass>, 
  InitialisationZero<MyClass>, 
  FunctionalitySendMail > MyOtherCompoundClass;

MyCompoundClass will now have three functions: Create, Init, and DoSomething, each of which is tailored to exactly how the user wants them to behave. By specifying MyClass in the template parameters, we can also specify what class the compound class works with. This is extremely useful for creating object factories and the like, especially when typelists are used (more about that another time).

But supposing you already know what class the CompoundClass is to work with? In that case, having to explicitly specify the class in a template parameter is just more clutter to the end-user. This is where "template template parameters" come in. No, that wasn't a typo.

Consider the following:

template 
<
    template < class Created > class CreationPolicy
> 
class CompoundClass : public CreationPolicy<MyClass>
{ 
};

Yikes, nested templates!

Indeed, by including this additional template declaration we allow the compound class to specify the template parameter itself, allowing us to declare the typedefs like this:

typedef CompoundClass< CreationNew, InitialisationZero, 
  FunctionalityHelloWorld > MyCompoundClass;
typedef CompoundClass< CreationPrototype, InitialisationZero, 
  FunctionalitySendMail > MyOtherCompoundClass;

Much neater.

Hopefully this will give an insight into the power of policy classes, as they provide a great deal of flexibility, and are not as restrictive in their use as some of the more standard OOP paradigms.

That's it for my first article. Short, yes, but hopefully it's opened a few eyes to some of the more obscure, and useful, uses for templates.

In my next article, I will be exploring more weirdness with templates, 'cause I'm like that.

Further reading

  • Modern C++ Design, by Andrei Alexandrescu, discusses Policy Class Systems along with many other "I didn't know you could do that!" C++ gems, and I strongly recommend it.

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

miscwriter
Systems Engineer
United Kingdom United Kingdom
My name is Chris Simpson and I'm great. If you like that sort of thing.

Comments and Discussions

 
Generalwww.boost.org & www.stlport.org PinmemberTW13-Jun-03 3:00 
QuestionCompiler recommendations? Pinmemberdog_spawn12-Jun-03 10:53 
AnswerRe: Compiler recommendations? PinmemberTW13-Jun-03 1:17 
GeneralRe: Compiler recommendations? PinmemberWilliam E. Kempf13-Jun-03 12:08 
GeneralRe: Compiler recommendations? PinmemberTW14-Jun-03 1:30 
GeneralRe: Compiler recommendations? PinmemberEd Din ar Qadiyyeh9-Sep-03 20:35 
AnswerRe: Compiler recommendations? PinmemberWilliam E. Kempf13-Jun-03 12:11 
GeneralGood link Pinmemberdog_spawn14-Jun-03 15:46 
GeneralRe: Good link PinmemberTW15-Jun-03 4:04 
GeneralRe: Good link Pinmemberdog_spawn15-Jun-03 4:32 
GeneralRe: Good link PinmemberTW15-Jun-03 5:13 
AnswerRe: Compiler recommendations? Pinmemberdmarcombes3-Sep-03 3:56 
GeneralBoring... Pinmemberdog_spawn3-Sep-03 7:48 
GeneralRe: Compiler recommendations? PinmemberGiles13-Sep-06 2:27 
Okay, gcc back in 97 was not much good either. Visual C++ 6 is coming up ten years old. You can't slag it off. Its not got as good conformance as 2005, as it never had the time to be.
 
This whole conversation is comparing apples to oranges. I use SunForte 5-U2 on Solaris, and its got sh*t conformance. You must be talking about SunWorkshop 11, which is now a free download.
 
You are talking out of your arse.
 

 

 

GeneralGreat stuff ... PinmemberMaximilien12-Jun-03 4:36 
GeneralRe: Great stuff ... Pinmembermiscwriter12-Jun-03 4:47 
GeneralRe: Great stuff ... Pinmemberyarp12-Jun-03 5:31 

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 | Terms of Use | Mobile
Web02 | 2.8.141030.1 | Last Updated 12 Jun 2003
Article Copyright 2003 by miscwriter
Everything else Copyright © CodeProject, 1999-2014
Layout: fixed | fluid