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

Fun With Templates: Part 1

By , 11 Jun 2003
 

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

About the Author

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

Sign Up to vote   Poor Excellent
Add a reason or comment to your vote: x
Votes of 3 or less require a comment

Comments and Discussions

 
You must Sign In to use this message board.
Search this forum  
    Spacing  Noise  Layout  Per page   
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 
First of all, don't assume everyone buy it from U.S. America usually enjoy better price compare to other region especially Asia. For this time, instead of recommend my company to upgrade, we are moving to LINUX with Intel C++ compiler step-by-step. We have developed MS product since MS.DOS.5, you call us anti-MS? We don't want to work with a product that get "upgrade" yearly, that have so much dependency (OS, .NET etc), we want a solution that is persist. With LINUX, it is not necessary to have the latest version to achieve a goal. We expect to see many problem arise migrating to LINUX, but having developing Windows based system for so many years, problems have never been less too.
 
Secondly, I am glad that VC++ is the best compiler, but I want to stress again being 100% compliance does not mean 100% productive. People use VC++ because of MFC/ATL, more advance user download platform SDK a MUST. Till date we still can use VC5 to work with latest SDK, although VC5 is hardly 80% compliance, you see what I mean? MS response is use .NET classes. But in this case people end up learning .NET framework, not C/C++. You should know blending VC++ with .NET is actually based on MS extension, and this is NOT what C++ is about.
 
I am not anti-MS, at some point I agreed that .NET is not a bad idea, but it is so much incompatible with C/C++ design. People should use .net nature language to consume the framework, never use C/C++. Try and see for yourself. Who knows in the future, product develop with VS need to have a chip embedded on the board to run?
 
We want to have control on our C/C++ projects, not Microsoft in anyway. I have read your work at BOOST, you should know very well what I meant, should we have boost.thread or system.thread? Your answer justify why blending C/C++ with .NET a mistake.
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 
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.

Permalink | Advertise | Privacy | Mobile
Web03 | 2.6.130516.1 | Last Updated 12 Jun 2003
Article Copyright 2003 by miscwriter
Everything else Copyright © CodeProject, 1999-2013
Terms of Use
Layout: fixed | fluid