您的位置:首页 > 编程语言 > C语言/C++

How to Generate C++ Class Template Definitions

2010-05-12 02:24 435 查看

c++ class template header and implementation(skeleton) definitions areoften hard to read, let alone to write. Especially when definingtemplate classes that are derived from base template classes, I oftenfind myself struggling for the correct syntax.

In this article, I will present a template-based source code generatorto produce C++ class template header implementation (skeleton)definitions in .hpp and .cpp files, based on a minimal yet functionalset of methods.A template is a way to specify generic code, with a placeholder for thetype. Note that the type is the only "parameter" of a template, but avery powerful one, since anything from a function to a class (or aroutine) can be specified in "general" terms without concerning yourselfabout the specific type. Yet. These details are postponed until youstart to use the template. You can consider templates to be compile-timepolymorphic, yet typesafe (in contrast to C MACROs).Function vs. ClassWhen talking about C++ templates, one should realize that there are, infact, two kinds of templates: function templates and class templates.The former are quite easy to implement, because they usually onlycontain the template(s) in their definition. As an example of a functiontemplate, here is a function that produces the minimum of twoarguments, 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;

}
[/code]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 whenyou 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
[/code]Or explicitly, as follows:
int x = max<int> (6, 42); // explicit template syntax
[/code]The C++ compiler will be able to determine—at compile time—where thecalls to this function template are made, which argument types are used,and hence which "expansions" of this function template have to begenerated (like a MACRO expansion) and then compiled and linked into anexecutable. All this is happening behind the scenes, of course, althoughtemplate expansion can take a lot of compiler and linker resource (asyou may find out when you start to use them more often).Class templates are similar to function templates in that the compilerwill determine at compile-time which expansions (or instantions) of theclass template are needed. The fact that they are classes and not merelyfunctions, makes the syntax a bit more difficult, however.Pop QuizEven if you're an experienced C++ class template user, could you tell mefrom the top of your head what the syntax would be of theimplementation skeleton for the copy constructor of a template classTDerived, which is derived from a template class TBase? You have 10seconds ...It turns out to be as follows:
template <class T> TDerived<T>::TDerived(const TDerived<T>& copy): TBase<T>(copy)
[/code]But I don't blame you if you couldn't come up with that right away. Ifyou did know the answer, then you probably don't need to read theremainder of this article, unless you're also interested in atemplate-based template header/source generator. That's what I made formyself, to help me remember.Canonical ClassBut before I want to continue with class templates, let's first talkabout a minimum useful class, sometimes also called a canonical class.By this I mean a class definition which is minimal (only a few keymethods), but still complete and useful. This means that the classshould at least contain the default constructor (without an argument),the destructor, a copy constructor, the assignment operator, the compareoperator and last—optionally—the stream operator (always useful whendebugging). When a class contains at least these methods, we can call ita 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. Makingsure that I produce a canonical class—especially when it comes totemplates, can make the difference between a nice but useless or anactual useful tool.As an example, here is the canonical class definition (header file) of aclass 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);

};
[/code]Canonical Class TemplateWe can modify the listing above to turn it into a canonical templateclass definition. Just like function templates, this means we have touse the <T> template syntax in a few places, and sometimes in morethan a few.Luckily, it's not that hard, and the result can be seen in the followinglisting:
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 emptyskeletons, 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 supportto get the class template syntax right.[/code]Derived TemplatesIf you've been able to keep up with me so far, then let's get to thefinal round: templates derived from other templates. Sometimes you justhave to derive your own custom class template TDerived from a basetemplate class TBase (sound familiar?).And just for your amusement (and mine), I've included the header listingfor 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)
c47a
;
// 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 emptyimplementation skeletons, which are defined as follows (empty becausethey're skeletons, but they still need to be implemented by theprogrammer, 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 secondthought? If you could, then you probably didn't need to read thisarticle, because the fun stuff is over. What remains is the descriptionof a little tool that I made for myself to actually produce and generatethe output listings that we've seen so far.Template TemplateIf you look closely at the listings presented so far, you can see apattern (believe me, there is logic behind this class template syntax).In fact, I have been able to produce two template files that can be usedto generate the template listings we've seen in this article. Thetemplate 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 astags used in the PageProducer components. That's actually the case,since I'm using a TPageProducer component (from the Internet tab) toexpand the above template into a true class template definitionheader—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 classtemplate as well as a derived class template. We only need to specifythree options: the class name, the (optional) base class name, and thetemplate character.[/code]HeadGen The utility HeadGen only requires the class name (the templatecharacteris T by default), as can be seen in Figure 1.For the base class, specify Base in the Class Name box leave theAncestor type box empty, and click on Generate. For the derived class,specify Derived in the Class Name box, Base in the Ancestor Type box andthen click on Generate again. In both cases, the T will be added asprefix automatically (files Base.hpp and Base.cpp will contain thedefinition for TBase).A very simple Borland C++Builder example program (to test the syntax ofthe 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 notonly (or just) the .hpp files. That's because the .cpp files are"expanded" (like MACROs) to the compiler, which must find them in orderto be able to use them.External TemplatesThe two template files are external files HeadGen.h (for the .hppheader) and HeadGen.c (for the .cpp source file). As an additionalbenefit, you can edit these templates and make sure your own copyrightstatements appear in them. Make sure to keep all #-tags intact, though,otherwise the template PageProducer won't be able to work correctlyanymore.
[code]from:http://www.devx.com/cplus/Article/20689/0/page/1
[/code]
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息