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 typedef
s 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 typedef
s 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.