c++ 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)
return X;
else
return Y;
}
T is the usual template character that is used to specify the typename,
which—at the time of definition—is 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 determine—at compile time—where 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.
Pop Quiz
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.
Canonical Class
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 last—optionally—the 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 class—especially 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:
class TBase
{
public:
// Constructors & Destructors
TBase(void);
TBase(const TBase& copy);
virtual ~TBase(void);
// Operator overloading
TBase& operator = (const TBase& other);
int operator == (const TBase& other) const;
// Output
friend ostream& operator << (ostream& os, const TBase& other);
};
Canonical Class Template
We can modify the listing above to turn it into a canonical template
class definition. Just like function templates, this means we have to
use the <T> template syntax in a few places, and sometimes in more
than a few.
Luckily, it's not that hard, and the result can be seen in the following
listing:
template <class T> class TBase
{
public:
// Constructors & Destructors
TBase(void);
TBase(const TBase<T>& copy);
virtual ~TBase(void);
// Operator overloading
TBase<T>& operator = (const TBase<T>& other);
int operator == (const TBase<T>& other) const;
// Output
friend ostream& operator << (ostream& os, const TBase<T>& other);
};
Just to let you know what the implementation looks like (the empty
skeletons, that is), take a look at the following listing:
// Constructors & Destructors
template <class T> TBase<T>::TBase(void) {}
template <class T> TBase<T>::TBase(const TBase<T>& copy) {}
template <class T> TBase<T>::~TBase(void) {}
// Operator overloading
template <class T> TBase<T>& TBase<T>::operator = (const TBase<T>& other) {}
template <class T> int TBase<T>::operator == (const TBase<T>& other) const {}
// Output
template <class T> ostream& operator << (ostream& os, const TBase<T>& other) {}
This is usually the place where I could do with a little help or support
to get the class template syntax right.
Derived Templates
If you've been able to keep up with me so far, then let's get to the
final round: templates derived from other templates. Sometimes you just
have to derive your own custom class template TDerived from a base
template class TBase (sound familiar?).
And just for your amusement (and mine), I've included the header listing
for the derived canonical class template definition below:
template <class T> class TDerived: public TBase<T>
{
public:
// Constructors & Destructors
TDerived(void);
TDerived(const TDerived<T>& copy);
virtual ~TDerived(void);
// Operator overloading
TDerived<T>& operator = (const TDerived<T>& other);
int operator == (const TDerived<T>& other) const;
// Output
friend ostream& operator << (ostream& os, const TDerived<T>& other);
};
Certainly this TDerived class template definition needs a list of empty
implementation skeletons, which are defined as follows (empty because
they're skeletons, but they still need to be implemented by the
programmer, of course).
// Constructors & Destructors
template <class T> TDerived<T>::TDerived(void): TBase<T>() {}
template <class T> TDerived<T>::TDerived(const TDerived<T>& copy): TBase<T>(copy) {}
template <class T> TDerived<T>::~TDerived(void) {}
// Operator overloading
template <class T> TDerived<T>& TDerived<T>::operator = (const TDerived<T>& other) {}
template <class T> int TDerived<T>::operator == (const TDerived<T>& other) const {}
// Output
template <class T> ostream& operator << (ostream& os, const TDerived<T>& other) {}
OK, who could already produce the above listing without a second
thought? If you could, then you probably didn't need to read this
article, because the fun stuff is over. What remains is the description
of a little tool that I made for myself to actually produce and generate
the output listings that we've seen so far.
Template Template
If you look closely at the listings presented so far, you can see a
pattern (believe me, there is logic behind this class template syntax).
In fact, I have been able to produce two template files that can be used
to generate the template listings we've seen in this article. The
template file for the class definition (typically inside a header file)
is defined as follows:
// File: <#class>.hpp
// Author: drs. Robert E. Swart>
// Date: <#date>
// Time: <#time>
// Version: 0.01
// Generated by: HeadGen (c) 1995-2001 by Bob Swart
(aka Dr.Bob - www.drbob42.com)
// Changes:
//
#ifndef <#class>_hpp
#define <#class>_hpp
#include <iostream.h>
<#includebase>
template <class <#templatechar>> class <#class> <#publicbase>
{
public:
// Constructors & Destructors
<#class>(void);
<#class>(const <#class><#template>& copy);
virtual ~<#class>(void);>
// Accessing functions
// Modifier functions
// Operator overloading
<#class><#template>& operator = (const <#class><#template>& other);
int operator == (const <#class><#template>& other) const;
// Streaming output
friend ostream& operator << (ostream& os, const <#class><#template>& other);
protected:
private:
};
#endif
Note the special #-tags. WebBroker developers may recognize these as
tags used in the PageProducer components. That's actually the case,
since I'm using a TPageProducer component (from the Internet tab) to
expand the above template into a true class template definition
header—with or without a template base class.
The same technique can be applied to the following template listing,
that can be used to produce the empty template skeleton implementations:
// File: <#class>.cpp
// Author: drs. Robert E. Swart
// Date: <#date>
// Time: <#time>
// Version: 0.01
// Generated by: HeadGen (c) 1995-2001 by Bob Swart
(aka Dr.Bob - www.drbob42.com)
// Changes:
//
#include "<#include>.hpp"
// Constructors & Destructors
template <class <#templatechar>> <#class><#template>::<#class>(void) <#base>
<#body>
template <class <#templatechar>> <#class><#template>::<#class>(const
<#class><#template>& copy) <#basecopy>
<#body>
template <class <#templatechar>> <#class><#template>::~<#class>(void)
<#body>
// Operator overloading
template <class <#templatechar>> <#class><#template>& <#class><#template>::operator = (const <
#class><#template>& other)
<#body>
template <class <#templatechar>> int <#class><#template>::operator == (const
<#class><#template>& other) const
<#body>
// Streaming output
template <class <#templatechar>> ostream& operator << (ostream&
os, const <#class><#template>& other)
<#body>
Again, the above listing can be used to produce a stand-alone class
template as well as a derived class template. We only need to specify
three options: the class name, the (optional) base class name, and the
template character.
HeadGen
The utility HeadGen only requires the class name (the template character
is T by default), as can be seen in
Figure 1.
For the base class, specify Base in the Class Name box leave the
Ancestor type box empty, and click on Generate. For the derived class,
specify Derived in the Class Name box, Base in the Ancestor Type box and
then click on Generate again. In both cases, the T will be added as
prefix automatically (files Base.hpp and Base.cpp will contain the
definition for TBase).
A very simple Borland C++Builder example program (to test the syntax of
the generated files) can be seen below:
//---------------------------------------------------------
#pragma hdrstop
#include "Base.cpp" // TBase
#include "Derived.cpp"; // TDerived
//--------------------------------------------------------
typedef TDerived<int> TintClass;
#pragma argsused
int main(int argc, char* argv[])
{
TintClass* Bob = new TintClass();
TintClass Swart = TintClass(*Bob);
if (*Bob == Swart) { *Bob = Swart; }
return 0;
}
//--------------------------------------------------------
Note that I needed to include the .cpp files of the templates, and not
only (or just) the .hpp files. That's because the .cpp files are
"expanded" (like MACROs) to the compiler, which must find them in order
to be able to use them.
External Templates
The two template files are external files HeadGen.h (for the .hpp
header) and HeadGen.c (for the .cpp source file). As an additional
benefit, you can edit these templates and make sure your own copyright
statements appear in them. Make sure to keep all #-tags intact, though,
otherwise the template PageProducer won't be able to work correctly
anymore.
from:
http://www.devx.com/cplus/Article/20689/0/page/1
posted on 2010-04-19 11:05
chatler 阅读(607)
评论(0) 编辑 收藏 引用 所属分类:
Template