++ class template header and implementation (skeleton) definitions are often hard to read, let alone to write. Especially when defining template classes that are derived from base template classes, I often find myself struggling for the correct syntax.
In this article, I will present a template-based source code generator to produce C++ class template header implementation (skeleton) definitions in .hpp and .cpp files, based on a minimal yet functional set of methods.
A template is a way to specify generic code, with a placeholder for the type. Note that the type is the only "parameter" of a template, but a very powerful one, since anything from a function to a class (or a routine) can be specified in "general" terms without concerning yourself about the specific type. Yet. These details are postponed until you start to use the template. You can consider templates to be compile-time polymorphic, yet typesafe (in contrast to C MACROs).
Function vs. Class
When talking about C++ templates, one should realize that there are, in fact, two kinds of templates: function templates and class templates. The former are quite easy to implement, because they usually only contain the template(s) in their definition. As an example of a function template, here is a function that produces the minimum of two arguments, without specifying the actual type of the arguments:
template <typename T>
T max(const T &X, const T &Y)
if (X > Y)
T is the usual template character that is used to specify the typename, whichat the time of definitionis unknown, and will be determined when you actually use the template in your source code. Here is an example:
int x = max(6, 42); // compiler determines T = int
float y = max(3.1415927, 2.20371); // compiler determines T = float
Or explicitly, as follows:
int x = max<int> (6, 42); // explicit template syntax
The C++ compiler will be able to determineat compile timewhere the calls to this function template are made, which argument types are used, and hence which "expansions" of this function template have to be generated (like a MACRO expansion) and then compiled and linked into an executable. All this is happening behind the scenes, of course, although template expansion can take a lot of compiler and linker resource (as you may find out when you start to use them more often).
Class templates are similar to function templates in that the compiler will determine at compile-time which expansions (or instantions) of the class template are needed. The fact that they are classes and not merely functions, makes the syntax a bit more difficult, however.
Even if you're an experienced C++ class template user, could you tell me from the top of your head what the syntax would be of the implementation skeleton for the copy constructor of a template class TDerived, which is derived from a template class TBase? You have 10 seconds ...
It turns out to be as follows:
template <class T> TDerived<T>::TDerived(const TDerived<T>& copy): TBase<T>(copy)
But I don't blame you if you couldn't come up with that right away. If you did know the answer, then you probably don't need to read the remainder of this article, unless you're also interested in a template-based template header/source generator. That's what I made for myself, to help me remember.
But before I want to continue with class templates, let's first talk about a minimum useful class, sometimes also called a canonical class. By this I mean a class definition which is minimal (only a few key methods), but still complete and useful. This means that the class should at least contain the default constructor (without an argument), the destructor, a copy constructor, the assignment operator, the compare operator and lastoptionallythe stream operator (always useful when debugging). When a class contains at least these methods, we can call it a canonical class.
Since I'm planning to produce a template header and source generator, it's important to realise what I should generate and what not. Making sure that I produce a canonical classespecially when it comes to templates, can make the difference between a nice but useless or an actual useful tool.
As an example, here is the canonical class definition (header file) of a class TBase:
// Constructors & Destructors
TBase(const TBase& copy);
// Operator overloading
TBase& operator = (const TBase& other);
int operator == (const TBase& other) const;
friend ostream& operator << (ostream& os, const TBase& other);