You are on page 1of 72

Page 1 of 72

1. Introduction
Motivation
Library design is language design. [Stroustrup] Course Goal To learn how to implement software libraries, such as STL, CGAL, LEDA, ..., that have a focus on algorithms and data structures. To learn advanced programming techniques in C++, such as templates, generic programming, object-oriented design, design patterns, and large-scale C++ software design. We will approach library design from different angles:
Generaldesignmethods:hereGenerativeProgrammingandDomainAnalysiswithFeatureDiagrams StudyingtheexpressivenessofthelibraryimplementationlanguageC++ Studyingexamplelibraries Studyingindividualandoftenoccurringproblems

In contrast to the software design of an application, the design for a software library is an obvious quality criterion for the user, i.e., software developer using the library. In addition, the library designer is more often faced with the problem of a greenfield design, i.e., the library designer does not know the specific needs of the (potentially many) library user. We like to emphasize here that the user's point of view of library design can differ quite drastically from the library designers point of view. The use of a library can be much easier than its implementation. A complicated implementation technique for a library does not necessarily make the library complex for the user. However, maintainability and the required training of the library developers have to be taken into account.

Generative Programming
Def. [pg. 5, Czarnecki00]
Generative Programming (GP) is a software engineering paradigm based on modeling software system families such that, given a particular requirements specification, a highly customized and optimized intermediate or end-product can be automatically manufactured on demand from elementary, reusable implementation components by means of configuration knowledge. So, generative programming has a strong focus on reuse. This is reflected in the analysis and design phase of the software development that is broken into two tracks: the domain engineering develops the design for the system families, and application engineering develops the design for a specific product created with specific members of the system families. So, domain engineering is design for reuse, and application engineering is design with reuse. We focus on domain engineering and later on domain engineering for algorithm libraries. Domain engineering breaks down into the following tasks:
Domain analysis:determinethescopeofthedomainanddefineasetofreusable,configurablerequirementsforthesystemsinthe domain. Domain design:developasystemsarchitectureandaproductionplan. Domain implementation:implementthereusableassets,fromcomponentsuptotheconfigurationknowledge.

In more detail from the domain engineering method DEMRAL [Czarnecki00]:


1. Domainanalysis 1. Domaindefinition 1. Goalandstakeholderanalysis 2. Domainscopingandcontextanalysis 1. Analysisofapplicationareasandexistingsystems 2. Identificationofdomainfeatures 3. Identificationofrelationshipstootherdomains 2. Domainmodeling 1. Identificationofkeyconcepts 2. Featuremodelingofthekeyconcepts,i.e.,identificationofcommonalities,variabilities,featuredependencies,andfeature interactions 2. Domaindesign

file://C:\Users\rmanoj\Desktop\Untitled.htm

1/13/2010

Page 2 of 72

1. Identificationandspecificationoftheoverallimplementationarchitecture 2. Identificationandspecificationofdomainspecificlanguages 3. Specificationoftheconfigurationknowledge 3. Domainimplementation

Besides [Czarnecki00] see also the Multi-Paradigm Design in [Coplien98] for details on domain analysis.

Feature Modeling
A feature model represents the common and the variable features of concept instances and the dependencies between the variable features. It consists of a feature diagram and some additional information described later. The feature diagram is a tree with a concept as root and features in each node. The following decorations on the tree edges are distinguished:

Mandatory features:allfeatures Optional features:anyfeaturecan havetobepresent. bechosenoptionally.

Alternative features:exactlyone Or-features:atleastonefeature hastobechosen. featurehastobechosen.

In principle, all combinations of decorations are allowed. In particular, the arcs indicating alternative or or-features do not have to cover all edges of a node. For example, two arcs could cover two groups, creating two sets of children. Some combinations are redundant and are normalized as follows:

Optional alternative features:Ifonefeatureinanalterniveis Optional or-features:Ifonefeatureinanor-featureisoptionalthenthe`at optionalthenallfeaturesinthatalternativeareoptional. least one feature'requirementbecomesmeaninglessandwenormalizetoa setofoptionalfeatures.

Features can be direct children of the concept root node or of other feature nodes. If they are direct children of another feature node then they are only considered if the parent feature has been chosen. Feature diagrams are a compact notation to record the set of feasible combinations of features of a concept. Let's consider an example:

In words: a car must have a car body, a transmission, an engine, and it can optionally pull a trailer. Note that although it looks like a physical decomposition, features can be arbitrary predicate statements, such as `maximum speed is 100mph', and the box `transmission' is read as `the car has a transmission'. In the second layer of the tree, we see the transmission can be either automatic or manual, but not both at the same time (alternative feature). The car must have an engine that is electric or gasoline powered. However, it can have both types of engines at the same time (or-feature). So the feature set encoded in this feature diagram is, written in a set notation:
{ {Car body, {Automatic}, {Electric}}, {Car body, {Automatic}, {Electric}, Pulls trailer},

file://C:\Users\rmanoj\Desktop\Untitled.htm

1/13/2010

Page 3 of 72

{Car {Car {Car {Car {Car {Car {Car {Car {Car {Car }

body, body, body, body, body, body, body, body, body, body,

{Manual}, {Manual}, {Automatic}, {Automatic}, {Manual}, {Manual}, {Automatic}, {Automatic}, {Manual}, {Manual},

{Electric}}, {Electric}, {Gasoline}}, {Gasoline}, {Gasoline}}, {Gasoline}, {Electric, Gasoline}} {Electric, Gasoline}, {Electric, Gasoline}} {Electric, Gasoline},

Pulls trailer}, Pulls trailer}, Pulls trailer}, Pulls trailer}, Pulls trailer},

We have some information associated with a feature diagram


Semantic description:Adescriptionforeachfeature.Informaltextoranyothersuitableformalismcanbeusedhere. Rational:Whyisthisfeatureinthismodel?Conditionsandrecommendationswhenandwhennottoselectavariablefeature. Stakeholders and client programs:Alistofstakeholders(user,customers,managers,etc.)whoareinterestedinthisfeature,andinthe caseofacomponentalistofclientprogramsthatneedthisfeature.(Suchalistcanbehelpfultoavoidunnecessaryfeaturebloat.) Exemplar systems:Ispossible,mentionknownsystemsthathavethisfeature. Constraints and default dependency rules:Dependencyrulescanbeexpressedbetweenanysetofvariablefeatures.Wehavemutualexclusion constraintsandrequire constraints.Defaultdependencyruleshelpinautomaticallyconfiguringasystem. Availability sites, binding sites, and binding mode:Availablitysitesdescribeswhen,where,andtowhomavariablefeatureisavailable, andbindingsitedescribeswhen,where,andbywhomafeaturemaybebound.Bindingmodedetermineswhetherafeatureis statically,changeably,ordynamicallybound. Open/closed attribute:Afeatureisopenifweexpectmorevariabledirectsubfeatures,forexample,thenumbertypespossibleforan elementinamatrixclassmightincreaseinthefuture.Otherwisewewouldmarkthefeatureasclosed. Priorities:Recordstherelevancefortheproject. Implementation strategies:Identifyingimplementationstrategiesforthisfeature(alreadypartofthedomaindesign).

We give some more options for the binding mode, i.e., the binding time of a variable feature:
Source time:C++templates,e.g.std::list<int> Compile time:C++functionoverloading,templates,Cpreprocessor-DNDEBUG Link time:makefileconfiguration Load time:dynamiclinklibraries Infrequent at run time:Sun'sHotSpottechnologyforJava Frequent at run time:variablevalues,virtualmemberfunctions

Brain Storming
Brain storming would be a possible technique to get a first set of concepts and features in the domain analysis. A variant of brain storming, known as MetaMind, uses written notes instead of voicing ideas freely in a group. Voicing ideas tends to hinder creativity since a major opinion or idea could dominate the session. For MetaMind, a neutral organizer writes a central question on the board. The participants have about 10 minutes to write their free associations on index cards, preferably single words or short phrases, each one on one card. The participants tend to be busy for the first few minutes. It is important to continue a few minutes past the first rush of obvious answers. Thereafter the cards are pinned on a board in an unorganized manner. Unclear cards have to be clarified, duplicates and synonyms are removed. Then, the organizer moderates a discussion about how to relate the cards to each other, for example commonalities, abstractions, etc. The outcome is a set of concepts and features, relations among feature that lead to feature diagram with annotations, for example, commonalities and variablities.

Feature Starter Sets


Another way of getting started are predefined feature starter sets. Specifically for our area of algorithm library design the feature starter sets for ADTs and for algorithms from [Czarnecki00] can be used. Here is the set for ADTs:
Attributes:NamedpropertiesofanADT,e.g.,sizeofacontainer.Featuresofanattributecanbe;mutableversusconst,volatileornot, storedorcomputed,internallyorexternallyowned. Data structures:UsedtoimplementtheADTontop. Operations:Accessorsandthecorealgebraoperations.Signature,i.e.,name,operands,operandtypes.Differentalternative implementations.Possibleoptimizationsandspecializations.Bindingmodeandbindingtime. Error detection, response, and handling:Pre-andpost-conditions,invariants.Exceptionsafety. Memory management:Heaporstack.Customorstandardallocation.Threadsafety.Persistence. Synchronization:Threadsafetyofshareddata. Persistency:StoringandrestoringastateoftheADTtoandfromdisk. Perspectives and subjectivity:DifferentviewsofstakeholdersordifferentclientprogramsontheADT.

file://C:\Users\rmanoj\Desktop\Untitled.htm

1/13/2010

Page 4 of 72

For container like ADTs we can also consider:


Element type:ThetypesmanagedbytheADT. Indexing:Aretheelementsaccessiblewithanindex,e.g.,withintegerkeysorsymbolickeys. Structure:Thestructureoftherepresentation,e.g.,matrix.

The feature starter set for algorithms:


Computational aspect:Thefunctionaldescriptionofthealgorithm,e.g.,textbookdescriptionorpseudocode.Possibleclassificationof severalalgorithmsintogroups,e.g.,searchingandsorting. Data access:TheinterfacetotheADTs,e.g.,iteratorsordataaccessors. Optimizations. Error detection, response, and handling. Memory management:Mightbeneededhereaswellforlocaldataoroptimizationpurposes. Parallelization:Relatedtooptimization,hereforspecifichardwareorsystemarchitectures.

Odds and Ends


From software engineering we list some general design goals one should keep in mind:
Flexibility Adaptability(toexistingenvironment) Extensibility Openness(tootherstandards/libraries) EaseofUse Smoothlearningcurve(numberofconcepts,structure) Uniformity Completeandminimalinterfaces Richandcompletefunctionality(isituseful?) Efficiency Spaceandtime O-calculus Practice(log(1000000)=7,andsqrt(sqrt(1000000))=31.6) Worstcase/averagecase Inputmodel,arethehardcasesrealistic? Correctness Documentation Hardbecauseofcomplexity Robustness Roundingerrors(ingeometric) Boundarycases,degeneratesituations(ingeometry) Modularity Maintainability ...

The famous Occam's razor:


"Pluralitasnonestponendasineneccesitate"or"pluralityshouldnotbepositedwithoutnecessity."

The words are those of the medieval English philosopher and Franciscan monk William of Ockham (ca. 1285-1349). It leads to the useful KISS (Keep It Simple, Stupid) principle that can be used to fend of "creeping featurism".
size=2width="100%"align=center> LutzKettner(<surname>@mpi-sb.mpg.de).LastmodifiedonTuesday,17-Jan-200617:53:41MET.

2. The C++ Language


Introduction
AbasicfamiliaritywithC++isassumed.Werepeatshortlyclasses,memberfunctions,constructorsanddestructor,derivation,virtualmember functions,andstaticclassmembers.Wecontinuethenwithanintroductiontotemplates.RecommendedintroductionsforC++are [Stroustrup97](advanced),[Lippman98](morebasic),[ISO-C++-98](theactualstandard;-).

file://C:\Users\rmanoj\Desktop\Untitled.htm

1/13/2010

Page 5 of 72

Classes and Member Functions


struct A { int i; };

WecallthetypeAaclass.IfwecreateavariableoftypeA,wecallthisvariableanobjectofclassA,whichisalsoknownasaninstantiationof classA.EachobjectoftypeAhasamembervariableiwhichcanbeaccessedwiththedotnotation:
int main() { A a; a.i = 5; }

C++providesaccesscontrolforclassmembers,whichcanbeeitherofprivate,protected,orpublic.Privatememberscanonlybeused withintheclass.Protectedmemberscanalsobeusedbyderivedclasses.Publicmemberscanbeusedfromeverywhere.Themembersin structarebydefaultpublic,themembersinclassarebydefaultprivate.ThusabovedefinitionofAisequivalentto:


class A { public: int i; }; Wewillusestructorclassinterchangeable,whicheverdefaultismoreconvenient.

A member function looks like a normal function declaration within a class definition. It is usually placed in a header file *.h.
class A { int i; // private public: int get_i(); void set_i( int n); };

Amemberfunctionsdefinitioniswrittenoutsideoftheclassdefinitionandusesthescopeoperator::tonamethefunctionwithintheclass.It isusuallyplacedinasourcefile*.C.Memberfunctionscanjustaccessallotherclassmembers.Toaccomplishthis,theC++compileradds automaticallyahiddenfunctionparameterthatpointstothecurrentobject.ThishiddenfunctionparameterisnamedthisandisoftypeA*.


int A::get_i() { return i; } void A::set_i( int n) { i = n; } Themembervariableiisnowinaccessiblefromtheoutsideandtheobjectcanonlybemanipulatedthroughitsmemberfunctions. int main() { A a; a.set_i(5); int j = a.get_i(); }

Forefficiency,functionsandmemberfunctions(collectivelycalledfunctionsinthefollowing)canbedeclaredinline,whichadvisesthe compilertoreplaceafunctioncalldirectlywiththefunctionbodyifpossible,insteadofcreatingafunctioncallandaseparatelycompiled functionbody.Smallinlinefunctionscanleadtofasterandsmallercode.Notsosmallinlinefunctionscanstillleadtofaster,butlargercode. Inlinefunctionshavetobedefinedbeforetheiruse,thustheirimplementationmovesusuallytotheheaderfile.Ifthememberfunctionis definedwithintheclass,theinlinedeclarationisautomaticallyassumed.


class A { int i; // private public: int get_i() { return i; } // both inline void set_i( int n) { i = n; } }; VariablescanbedeclaredconstinC++.Tocheckconstcorrectnessacrossclassesandmemberfunctions,amemberfunctionhastobe declaredconstifitdoesnotchangethemembervariableoftheclass.Otherwise,amemberfunctioncannotbecalledforanobjectthatis declaredconst.SothecompletedefinitionofourexampleclassAlooksasfollows:(Seealsoconstcorrectness.) class A { int i; // private public: int get_i() const { return i; } // inline and const void set_i( int n) { i = n; } // inline }; int main() { A a; a.set_i(5); int j = a.get_i(); const A a2; // a2.set_i(5); } // uninitialized constant // this is forbidden by C++ type system // So, const A is currently pretty useless.

Constructors, Assignment, and Destructor


Constructorsarespecial`memberfunctions'ofwhichexactlyoneiscalledautomaticallyatcreationtimeofanobject.Whichonedependson

file://C:\Users\rmanoj\Desktop\Untitled.htm

1/13/2010

Page 6 of 72

theformofthevariabledeclaration.Symmetrically,thereexistsadestructorwhichisautomaticallycalledatdestructiontimeofanobject.

Three types of constructors are distinguished: default constructor, copy constructor, and all others (user defined). A constructor has the same name as the class and it has no return type. The parameter list for the default and the copy constructor are fixed. The user defined constructors can have arbitrary parameter lists following C++ overloading rules.
class A { int i; // private public: A(); // A( const A& a); // A( int n); // ~A(); // };

default constructor copy constructor user defined destructor

int main() { A a1; // calls the default constructor A a2 = a1; // calls the copy constructor (not the assignment operator) A a3(a1); // calls the copy constructor (usual constructor call syntax) A a4(1); // calls the user defined constructor } // automatic destructor calls for a4, a3, a2, a1 at the end of the block

Thecompilergeneratesamissingdefaultconstructor,copyconstructor,ordestructorautomatically.Thedefaultimplementationofthedefault constructorcallsthedefaultconstructorsofallclassmembervariables.Thedefaultconstructorisnotautomaticallygeneratedifother constructorsareexplicitlydeclaredintheclass(exceptanexplicitdeclaredcopyconstructor).Thedefaultcopyconstructorcallsthecopy constructorofallclassmembervariables,whichperformsabitwisecopyforbuilt-indatatypes.Thedefaultdestructordoesnothing.All defaultimplementationscanbeexplicitlyinhibitedbydeclaringtherespectiveconstructor/destructorintheprivatepartoftheclass.

Constructors initialize member variables. A new syntax, which resembles constructor calls, allows to call the respective constructors of the member variables (instead of assigning new values). The constructor call syntax extends also to built-in types. The order of the initializations should follow the order of the declarations, multiple initializations are separated by comma.
class A { int i; // private public: A() : i(0) {} A( const A& a) : i(a.i) {} A( int n) : i(n) {} ~A() {} };

// // // //

default constructor copy constructor, equal to the compiler default user defined destructor, equal to the compiler default

Usually,onlythedefaultconstructor(ifthesemanticsarereasonable),andsomeuserdefinedconstructorsaredefinedforaclass.Assoonas theclassmanagessomeexternalresources,e.g.,dynamicallyallocatedmemory,thefollowingfourimplementationshavetoworktogetherto avoidresourceallocationerrors:defaultconstructor,copyconstructor,assignmentoperator,anddestructor.Notethatthecompilerwould createadefaultimplementationfortheassignmentoperatorifitisnotdefinedexplicitly.Seethefollowingexampleand[Item11and17, Meyers97].Notetheuseofthis,thepointertothecurrentobject.(seealsoBuffer.C)


class Buffer { char* p; public: Buffer() : p( new char[100]) {} ~Buffer() { delete[] p; } Buffer( const Buffer& buf) : p( new char[100]) { memcpy( p, buf.p, 100); } void swap( Buffer& buf) { char* tmp = p; p = buf.p; buf.p = tmp; } Buffer& operator=( const Buffer& buf) { // Check for self-assignment, but its only an optimization if ( this != & buf) { // In general: perform copy constructor and destructor // Make sure that self-assignment is not harmful. Buffer newbuf( buf); // create the new copy swap( newbuf); // exchange it with 'this' // the destructor of newbuf cleans up the data previously // stored in 'this'. } return *this; } };

Automatic Conversion and the explicit Keyword for Constructors


Auser-definedconstructorwithasingleargumentdefinesaconversionbetweentwotypes;thetypeoftheargumentandtheclassthe constructorbelongsto.TheC++compilerisallowedtoperformthisconversionautomaticallytofindamatchingfunctioncall.
struct Buffer { Buffer( int n); // construtor to allocate n bytes for buffer

file://C:\Users\rmanoj\Desktop\Untitled.htm

1/13/2010

Page 7 of 72

// ... }; void rotate( Buffer& buf); // a function to rotate buffer cyclically int main() { rotate( 5); // oops, a temporary Buffer initialized with 5 will be created }

Theseautomaticconversionscanbeasourceoferrorsthataredifficulttospot.Itisadvisedtoforbidthemwiththenewkeywordexplicit.
struct Buffer { explicit Buffer( int n); // ... };

Ambiguity between Function-Style Cast and Declaration


TheconstructornotationanditsimpliedfunctionstylecastgivesrisetoacoupleofambiguitiesinC++andtheheavilyoverloadeduseof parentheses.Incaseofambiguitiesbetweenastatementandadeclaration,thecompilerchosesthedeclaration.
struct S { S(int); }; void foo( double d) { S v( int(d)); // function declaration S w = S( int(d)); // object declaration S x(( int(d))); // object declaration } visafunctiondeclarationbecausetheparenthesesarounddareredundant,soS v( int d);isobviouslyafunctiondeclarationandnotan objectoftypeSthatgetsinitializedwithdcastedtoint.

To get the second interpretation we have to disambiguate the declaration explicitly, either by using the other intializer notation as for w or by adding parentheses that exclude the function declaration as for x. (see also decl.C)

Derivation
IfwederiveaclassBfromabaseclassA,BinheritsallmembervariablesandallmemberfunctionsfromA,butitcanaccessonlythosethatare notprivate.Weconsideronlypublicinheritancehere(inheritancecanalsobequalifiedasprivate).
class B : public A { int j; }; Bhasnowtwointegervariablemember.ObjectsofclassBcanbeassignedtoobjectsofclassA.Doingso,theyloosetheiradditionalmember variablej. int main() { B b; A a = b; }

Constructorsanddestructorarenotinherited.Butthedefaultimplementationsofthederivedclasscallautomaticallytherespective implementationsofthebaseclass.Onlytheadditional,userdefinedconstructorsaremissingandmustberepeated.Callingabaseclass constructorexplicitlyfollowsthesamesyntaxasthemembervariableinitialization.


class B : public A { int j; public: B( int n) : A(n) {} B( int n, int m) : A(n), j(m) {} };

Thefirstconstructorandthedefaultconstructorleavethevalueofjuninitialized.Wesolvethisinthefollowingexampleandusedefault valuestoimplementthethreeconstructorsinone.
class B : public A { int j; public: B( int n = 0, int m = 0) : A(n), j(m) {} };

Virtual Member Functions


VirtualmemberfunctionsandderivationprovidethebackboneofflexibilityinC++fortheobject-orientedparadigm.Abaseclassdefinesan interfacewithvirtualmemberfunctions,hereapurevirtualmemberfunction.
struct Shape { virtual void draw() = 0; };

WederivedifferentconcreteclassesfromShapeandimplementthememberfunctiondrawforeachofthem.
struct Circle : public Shape { void draw();

file://C:\Users\rmanoj\Desktop\Untitled.htm

1/13/2010

Page 8 of 72

}; struct Square : public Shape { void draw(); };

Wecannotcreateanobjectofaclassthatcontainspurevirtualmemberfunctions,butwecanhavepointerofthistypeandwecanassign pointerofthederivedtypestothem.Ifwecallamemberfunctionthroughthispointer,theprogramfiguresoutatruntimewhichmember functionismeant,Circle::draworSquare::draw.


int main() { Shape* s1 = new Circle; Shape* s2 = new Square; s1->draw(); // calls Circle::draw s2->draw(); // calls Square::draw }

Thisruntimeflexibilityisachievedwithavirtualfunctiontableperclass.(dispatchtablewithfunctionpointers).Eachobjectgetsanadditional pointerreferringtothistable.Thus,eachobjectknowsatruntimeofwhichtypeitis,whichisalsousedfortheruntimetypeinformationin C++.Theseextracosts,additionalpointerandonemoreindirectionforafunctioncall,areonlyimposedonobjectswhichclassorbaseclasses usevirtualmemberfunctions.

Since we don't know the size of the actually allocated objects any more, we also have to use a virtual member function to delete the objects properly. It is sufficient to define a virtual, empty destructor in the base class. (see also Shape.C)
struct Shape { virtual void draw() = 0; virtual ~Shape() {} }; // ... the derived shape classes int main() { Shape* s1 = new Circle; Shape* s2 = new Square; s1->draw(); // calls Circle::draw s2->draw(); // calls Square::draw delete s1; delete s2; }

Static Class Members


Staticmembervariablesbelongtotheclass,nottotheobject.Theycanbeaccessedfromoutsidetheclasswiththescopeoperator.For example,aclassthatcountshowmanyobjectsofitstypehavebeencreatedlooksasfollows:(seealsoCounter.C)
#include <iostream.h> struct Counter { static int counter; Counter() { counter++; } }; int Counter::counter = 0; int main() { Counter a; Counter b; Counter c; cout << Counter::counter << endl; }

Notethatanexplicitdefinitionofthestaticmemberoutsideoftheclassisneeded.Thisdefinitionissupposedtoshowupinonlyone compilationunit(muchlikeaglobalvariable).Thus,thedefinitionisusuallyina*.Csourcefile.

Static member variables are guaranteed to be initialized before main gets executed. The order of initialization for multiple static member variables is specified to be in the order of declaration in a single compilation unit, but the order is unspecified between different compilation units. This implies in particular that a class which relies on the proper initialization of a static member variable (such as our Counter example) cannot be used as a type of a static member variable in another compilation unit (see [Item 47, Meyers97] for how to get around this restriction with local static variables in global functions). A static variable can be used to initialize a library automatically, see Automatic Library Initialization and Housekeeping. A member function exists already only once per class, but it has a pointer (this) to the current object hidden in its parameter list. A static member function omits this pointer. It is called like a normal function using the scope operator. A static member function cannot access member variables of the object. It can only access static member variables.
struct A { static int i; int j; static void init(); };

file://C:\Users\rmanoj\Desktop\Untitled.htm

1/13/2010

Page 9 of 72

void A::init() { i = 5; // fine // j = 6; // is not allowed } int main() { A::init(); assert( A::i == 5); }

Templates
TemplatesprovidethebackboneofflexibilityinC++forthegeneric-programmingparadigm.Theirflexibilityisresolvedatcompiletimethus retainingefficiency.

C++ supports two kinds of templates: class templates and function templates. Templates are incompletely specified components in which a few types are left open and represented by formal placeholders, the template arguments. The compiler generates a separate translation of the component with actual types replacing the formal placeholders wherever this template is used. This process is called template instantiation. The actual types for a function template are implicitly given by the types of the function arguments at instantiation time. Therefore, all template arguments must be used somewhere in the function parameter list. An example is a swap function that exchanges the value of two variables of arbitrary types.
template <class T> void swap( T& a, T& b) { T tmp = a; a = b; b = tmp; } int main() { int i = 5; int j = 7; swap( i, j); }

// uses "int" for T.

Theactualtypesforaclasstemplateareexplicitlyprovidedbytheprogrammer.Anexampleisagenericlistclassforarbitraryitemtypes.
template <class T> struct list { void push_back( const T& t); // append t to list. }; int main() { list<int> ls; // uses "int" for T. ls.push_back(5); } Definingpush_backoutsideofthelistclassrequirestherepetitionofthetemplatedeclarationtemplate <class T>,andthenameof thelistforthescope,whichislist<T>.Forthenamingconvention,listisaclasstemplate,whilelist<T>isatemplateclass,(inparticular, listisatemplate,andlist<T>isaclass). template <class T> void list<T>::push_back( const T& t) { ... }

The C++ compiler uses pattern matching to derive automatically the template argument types for functions template. Consider for example a function template that works only for lists:
template <class T> void foo( list<T>& ls);

Template arguments can have default values, e.g., a stack class built with our list. Note the space between the two > >. Otherwise this would be parsed as the right-shift operator.
template <class T, class Container = list<T> > struct stack { ... };

Besides type parameters, class templates can also have builtin integral types as template parameters. In that case, the template arguments must be constant expression for that integral type. A useful example are points of constant dimension. The parameter is determined at compile time and thus constant. We can use it to declare a fixed size array of coordinates for the point. Features like default arguments and specialization (see below) work also on this kind of parameter.
template <int dim> struct Point { double coordinates[dim]; // coordinate array // ... }; int main() { Point<3> // a point in 3d space }

file://C:\Users\rmanoj\Desktop\Untitled.htm

1/13/2010

Page 10 of 72

The C++ standard aims for separate compilation of templates, but this is not available in most compilers today. The current model for templates is that all source code including definitions goes into the header files.

"Lazy" Implicit Instantiation


Asalreadymentionedabove,theprocessofusingafunctiontemplateforcesthecompilertoinstantiatethetemplate,whichismoreprecisely calledimplicit instantiation.Thereisalsoanexplicitinstantiation,whichwedonotuseanddonotmentionanyfurther.

Member functions of class templates are also instantiated implicitly. Assume that we want to add a sort member function to our list template from above. Quicksort is not efficient on lists (needs an extra container), so our specialized member function realizes, for example, a more efficient merge sort. For a sort function, the item type of the list needs to be comparable. But lists in general are also useful for types that are not comparable. However, this causes no problem in C++, since C++ is not allowed to compile the sort member function if it is not instantiated somewhere. And the compiler is not allowed to complain about the missing comparison operator in the sort member function. In consequence, we can use the list class with a type that is not comparable, as long as we do not try to sort this list somewhere in our program. In the chapter on the STL we will see examples where this useful to implement generic adapters for iterators. This behavior is restricted to class templates.

Member Templates
Memberfunctionsarebasicallynormalglobalfunctions(withsomesyntacticsugarandcompilersupportforthethispointer).Thus,function templateswereeasytoextendtomemberfunctionsandevenconstructors(althoughlateinthestandardizationprocess).

The small convenience class pair<T1,T2> from the STL makes use of a member template constructor. The class pair<T1,T2> contains a member variable of type T1 and a member variable of type T2, i.e., a tuple (first,second) with types (T1,T2). The default copy constructor allows the construction of a pair, if the types for the template arguments are exactly the same. However, in C++ are several automatic conversions possible, for example mutable to const, pointer of derived class to pointer of base class, short to int, etc. It would be nice, if we could create one pair from another pair, if the types T1 and T2 would be assignable from one pair to the other pair. The solution is a template constructor accepting all pairs. The actual construction of its member variable compiles only if this construction is permitted.
template <class T1, class T2> struct pair { T1 first; T2 second; pair() {} // don't forget the default constructor if there are also others // template constructor template <class U1, class U2> pair( const pair<U1,U2>& p); };

Now,let'sassumewewanttodefinethisconstructornotinline(however,inlinewouldbepreferablehere).Hereis,howwenestthetwo templatedeclarations.(seealsopair.C)
template <class T1, class T2> template <class U1, class U2> pair<T1,T2>::pair( const pair<U1,U2>& p) : first( p.first), second( p.second) {}

Specialization and Partial Specialization


Letusassumewehaveagenericvectorclassvector<T>thatisjustfineforthegeneralcase,butforbooleanswecoulddobetterwithabit vector.Wecanwriteaspecializedclassforbooleans.
template <> struct vector<bool> { // specialized implementation }; Thecompilermatchesvector<bool>automaticallywiththisspecialization.Theemptytemplatedeclarationwaspreviouslysuperfluous,but

isnowmandatory.

Now suppose, vector has a second template argument for a memory allocator (which it does in the STL, but hidden by a default setting). The resulting partial specialization is still a template.
template <class Allocator = std::allocator> struct vector<bool,Allocator> { // partially specialized implementation };

Specializationandpartialspecializationworkalsoforfunctiontemplates.Sincewehavealreadymentionedpatternmatchingforfunction

file://C:\Users\rmanoj\Desktop\Untitled.htm

1/13/2010

Page 11 of 72

templates,itmightnotbesuchasurprise.However,itneedstobeclarified,howtheresultingoverloadingofthefunctionnamegetsresolved. Thegeneralruleofthumbisthatthecompilertriestoinstantiateallfunctiontemplatesthatcanmatchthefunctioncall,anditchoosesthe `mostspecific'instantiation.Ifthereismorethanone`mostspecific'instantiation,itisreportedasanambiguityerror.Thebadnewshereis thatasoundtypetheoryasknownfromfunctionallanguagesismissinghere.

Local Types and Keyword typename


Besidesvariablesandmemberfunctions,classescanalsocontainenum'sandtypes.Theyareaccessedwiththescopeoperator`::'.
template <class T> struct list { typedef T value_type; }; int main() { list<int> ls; list<int>::value_type i; // is of type int } LetusassumeaclassXusesacontainer,suchaslist<T>.NowclassXneedsthevaluetypeTofthecontainer,whichwehavealready preparedwiththetypedefinthelistclasstemplate.Forconvenience,weuseatypedefandthesamenameinclassX. template <class Container> struct X { typedef Container::value_type value_type; // not correct // ... }; ButhowcanthecompilerknowthatContainer::value_typeisactuallyatypeandnotastaticmembervariablewithoutknowingthe actualtypeforContainer,i.e.,beforeactuallyinstantiatingthetemplate?Itdoesnot.Thesolutionisthenewkeywordtypename.By

default,thecompilerassumesthatincaseofsuchambiguitiesthetokenisnotatype.Ifitisatype,wecansaysoexplicitlywiththenew keyword.Thus,thecorrectexamplesis:
template <class Container> struct X { typedef typename Container::value_type value_type; // ... }; Thekeywordtypenameisusedtoindicatethatthenamefollowingthekeyworddoesinfactdenotethenameofatype.However,onecannot justliberallysprinklecodewithtypenames.Moreprecisely,onemustusethekeywordtypenameinfrontofanamethat:

1. denotesatype;and 2. isqualified:i.e.,itcontainsascopeoperator`::';and 3. appearsinatemplate;and 4. isnotusedinalistofbase-classesorasanitemtobeinitializedbyaconstructorinitializerlist,and 5. hasacomponentleftofascoperesolutionoperatorthatdependsonatemplateparameter. Furthermore,oneisonly allowedtousethekeywordinthissenseifthefirstfourapply.Toillustratethisrule,considerthiscodefragment:


template<class T> struct S : public X<T>::Base { // no typename, because of 4 S(): X<T>::Base( // no typename, because of 4 typename X<T>::Base(0)) {} // typename needed X<T> f() { // no typename, because of 2 typename X<T>::C *p; // declaration of pointer p, typename needed X<T>::D *q; // no typename ==> multiplication! } X<int>::C *s_; // typename allowed but not needed }; struct U { X<int>::C *pc_; };

// no typename, because of 2

Dynamic and Static Polymorphism


Polymorphismreferstotheabilityofasinglepieceofcodetoworkwithmultipletypes.C++supportstwokindsofpolymorphism:dynamic (runtime)polymorphismthroughvirtualfunctionsandstatic(compiletime)polymorphismthroughtemplates.Dynamicpolymorphismisin centralroleinobject-orientedprogrammingwhilestaticpolymorphismisattheheartofgenericprogramming.

Let us look at the shape example we saw earlier:


struct Shape { virtual void draw() = 0; virtual ~Shape() {} }; struct Circle : public Shape { void draw(); }; struct Square : public Shape { void draw();

file://C:\Users\rmanoj\Desktop\Untitled.htm

1/13/2010

Page 12 of 72

};

Nowwehavetwowaysofwritingasinglefunctionthatworksforcircles,squares,andanyotherclassesderivedfromShape:
void display (const Shape& s) { s.draw(); } template <class T> void display (const T& s) { s.draw(); } // dynamic polymorphism

// static polymorphism // T does not need to be derived from Shape

Inthiscase,dynamicpolymorphismismoreappropriate.Theproblemswithstaticpolymorphismare: Heterogeneousdynamiccollectionsaredifficulttohandle.Forexample,adrawingclassmighthavealistofitscomponentshapes:
class Drawing { list<Shape*> components; // ... } // relies on dynamic polymorphism

Codebloat.Thereisaseparateinstanceofdisplay<T>foreachtypeT. Noseparatecompilation.Compilationofdisplay<T>needsthedefinitionofT. Templateshavetheiradvantages,too.Letuslookattheswapexample:


template <class T> void swap( T& a, T& b) { T tmp = a; a = b; b = tmp; } Thebasicoperationsusedbyswaparecopyconstructorandassignment.Todothiswithvirtualfunctions,weneedabaseclasswithvirtual

versionsofcopyconstructorandassignment.Becauseaconstructorcannotbevirtualandvirtualassignmenthasitsproblems(see[Gillam98]), weusenormalmemberfunctions:
struct Swappable { virtual Swappable* clone() const =0; virtual Swappable& assign(const Swappable& rhs) =0; virtual ~Swappable() {}; } void swap (Swappable& a, Swappable& b) { Swappable* tmp = a.clone(); a.assign(b); b.assign(*tmp); delete tmp; } NowswapworkswithanytypethatisderivedfromSwappableanddefinescloneandassignappropriately.Thisisclearlymoreawkward tousethantheswaptemplate.Otherproblemswithdynamicpolymorphismare:

Built-intypescannotbehandleddirectly. Inefficiency.(Virtualfunctioncalloverhead,noinlining.) Statictypecheckingiscompromised.Forexample,tryingtoswapobjectsoftwodifferenttypescannotbedetectedatcompiletime.

Name Spaces
As a library developer we do not own the universe, for example, for identifier names. An application programmer most likely uses more than one library and create additional identifier names. It frequently happened that common names such as min, max, swap, or Byte have been defined in more than one library with surprising results when those libraries where used together. Assume a header file "a.h" contains the macro definition
#define Byte unsigned char;

and another header file "b.h" defines


typedef unsigned char Byte;

What happens if we include both header files, does it depend on the order of inclusion? A common way out of this dilemma was and still is the use of a common prefix for all identifiers of one library. The prefix is typically a short abbreviation such as std_, CGAL_, Q for the Qt GUI interface, and it is supposely different than all other prefixes. In C++ we have the new concept of name spaces. They act as a scope and group identifiers together. They can be extended anytime. An example:
namespace CGAL { int max( int a, int b); class A; void foo( const A& a); } // ends the namespace CGAL

file://C:\Users\rmanoj\Desktop\Untitled.htm

1/13/2010

Page 13 of 72

We could use the above declarations in the following way:


int main() { int i = CGAL::max( 3, 4); A a; // assumes that we have also seen the full definition of A somewhere CGAL::foo( a); }

So far, the name space scope CGAL:: with the so-called scope operator :: has just replaced the prefix in user code. However, within the name space itself we don't have to repeat the scope all the time. Name lookup happens following the name space scope from the inside to the outside. The global name space scope is just denoted :: and can be used to name identifiers in the global scope that also exist in the current local scope. There is an alternative possebility for calling the function foo, see this example instead:
int main() { A a; foo( a); }

We just omitted the scope, but now the compiler also examines the argument types and includes the scopes of the argument types for name lookup searches. This is well known in C++ under the name Koenig lookup. All the standard C and C++ library has been enclosed in the std namespace. One can import a whole namespace or just selected identifiers into the current namespace with the using declarative:
using namespace std; using std::vector;

C Preprocessor: Include Guards and assert Macro


C++ has inherited from C its preprocessor, a separate phase of the compiler that processes its own language before the compiler looks at the (transformed) source code. We discuss only a few aspects of the preprocessor, largely because it has almost become superfluous with the new language elements of C++, namely constants and templates.

Symbolic Constants
One can define symbolic constants, such as:
#define M_PI 3.14159265358979323846 #define CGAL_CFG_NO_KOENIG_LOOKUP 1

Each of these definitions defines a replacement rule, where the identifier following the #define gets literally replaced with the text following it. Note that this replacement happens on a text processing basis only, the preprocessor does not know about classes, protection, namespaces and scopes!

Include Guards
The preprocessor has control structures that can control which parts of the source code are compiled and which parts are excluded from the compiler.
assert

Macro

3. STL and Generic Programming


Introduction
TheStandard Template Library(STL)fallsintotheclassoffoundationlibraries.Itprovidesbasicdatatypes,suchaslist,vector,set,and map,anditprovidesbasicalgorithms,suchasfindandsort.TheSTLispartoftheC++StandardLibrary,butnotalltemplatesinthestandard librarybelongtotheSTL,forexample,stringsarenotpartoftheSTL.Historically,theSTLstartedasanAdalibrary[Musser89].Itbecame widelyrecognizedasthereport[Stepanov95]startedcirculatingintheC++standardizationcommittee.TheSTLbecameinaslightlymodified formpartoftheC++standard.ThesemodificationsmakethestandardversionincompatibletothefirstSTL,butdespitethatitisstillcalledthe STL.Agoodup-to-dateintroductionandreferencefortheSTLcanbefoundin[Austern98].ThereferencepartisalsoavailableonlinefromSGI, see[SGI-STL].

The programming paradigm underlying STL is called generic programming. Here is one definition [Jazaeri98]:
Generic programming is a sub-discipline of computer science that deals with finding abstract representations of efficient algorithms, data structures, and other software concepts, and with their systematic organization. The goal of generic programming is to express algorithms and

file://C:\Users\rmanoj\Desktop\Untitled.htm

1/13/2010

Page 14 of 72

data structures in a broadly adaptable, interoperable form that allows their direct use in software construction. Key ideas include: Expressing algorithms with minimal assumptions about data abstractions, and vice versa, thus making them as interoperable as possible. Lifting of a concrete algorithm to as general a level as possible without losing efficiency; i.e., the most abstract form such that when specialized back to the concrete case the result is just as efficient as the original algorithm. When the result of lifting is not general enough to cover all uses of an algorithm, additionally providing a more general form, but ensuring that the most efficient specialized form is automatically chosen when applicable. Providing more than one generic algorithm for the same purpose and at the same level of abstraction, when none dominates the others in efficiency for all inputs. This introduces the necessity to provide sufficiently precise characterizations of the domain for which each algorithm is the most efficient.

Concept and Model


Considerourfirstexampleofafunctiontemplate,swap:
template <class T> void swap( T& a, T& b) { T tmp = a; a = b; b = tmp; }

Whenthetemplateisinstantiated(bycallingthefunction)theplaceholderTbecomesanactualtype.However,compilationcanonlysucceed ifthisactualtypehasanassignmentoperatorandacopyconstructor.Thefunctioncouldhavebeenimplementedusingadefaultconstructor andassignment,butthecopyconstructorismorelikelytoexistthanthedefaultconstructor(giventhattheassignmentoperatorisrequired anyway).

We can distinguish between syntactic requirements and semantic requirements. The syntactic requirements are the assignment operator and the copy constructor in our example. If an actual type fails to comply with these requirements a compilation error points that out. The semantic requirements are that the copy constructor and the assignment operator should actually copy the values, should be side effect free, and in general should behave according to the C object model, e.g., tmp = y; x = tmp; should give you the same as x = y;. Remember that these are user defined functions. Semantic requirements are not checkable at compile time. Instead of documenting requirements always in all detail, it is convenient to group them in often used combinations. We call these collections of requirements concepts. The concept for the swap function parameter is called Assignable. If an actual type fulfills the requirements of a concept, it is a called a model for this concept. In our example, int is a model of the concept Assignable.

Common basic concepts


Concept
Assignable

Syntactic requirements copyconstructor assignmentoperator equalityandinequalityoperator ordercomparisonwithoperators<, <=, >=, and >

Default Constructible defaultconstructor Equality Comparable LessThan Comparable

A regular type is one that is a model of Assignable, Default Constructible, Equality Comparable, and one in which these expressions interact in the expected way, for example, for x = y; we may assume that now x == y true is. In general, concepts factor out common signature and behavior for template arguments. One can think of a concept as the `greatest common denominator' of all types for which a function template is supposed to work. Of course, the function has then to be implemented using only the operations specified in the concept. In analogy to the object-oriented paradigm, concepts correspond to virtual base classes, and models correspond to derived classes. However, there is the important difference that concepts are nowhere explicitly coded in the language. They are only communicated in documentations. This is a maintenance disadvantage, but also an advantage, because it avoids the coupling of a common base class. A common base class needs a header file and all derived classes have to agree on this single header file, linking, etc. In general, the flexibility is resolved at compile time which gives us the advantages of strong type checking and inline efficiency where needed. If runtime flexibility is needed, the generic data structures and algorithms can be parameterized with a base class used in the object-oriented programming to get the runtime flexibility.

Generic Algorithms Based on Iterators


Algorithmicabstractionisakeygoalingenericprogramming[Musser89].Oneaspectistoreducetheinterfacetothedatatypesusedinthe algorithmtoasetofsimpleandgeneralconcepts.OneofthemistheIteratorconceptinSTLwhichisanabstractionofpointers.Iterators

file://C:\Users\rmanoj\Desktop\Untitled.htm

1/13/2010

Page 15 of 72

servetwopurposes:Theyrefertoanitemandtheytraverseoverthesequenceofitemsthatarestoredinadatastructure,alsoknownas container classinSTL.Fivedifferentcategoriesaredefinedforiterators:input,output,forward,bidirectionalandrandom-accessiterators, accordingtothedifferentpossibilitiesofaccessingitemsinacontainerclass.TheusualC-pointerreferringtoaC-arrayisamodelfora random-accessiterator.

The following table shows the different iterator concepts and the refinement relation between them and the basic concepts (see above). The syntactic requirements are only sketched here, see [ISO-C++-98, SGI-STL] for the full requirements.
Concept
Trivial Iterator Input Iterator Output Iterator Forward Iterator

Refinement of
Assignable,Equality Comparable Trivial Iterator Assignable Input Iterator,Output Iterator,Default Constructible

Syntactic requirements
operator*() operator->() operator++(),... operator*(),operator++()...

...
operator--(),... operator+(),operator+=(),operator-(), operator[](),...

Bidirectional Iterator Forward Iterator Bidirectional Iterator,LessThan Random Access Iterator Comparable

Sequences of items are specified by a range [first,beyond) of two iterators. This notion of a half-open interval denotes the sequence of all iterators obtained by starting with the iterator first and advancing first until the iterator beyond is reached, but it does not include beyond. The iterator beyond is also referred to as the past-the-end position. A container class is supposed to provide a member type called iterator, which is a model of the Iterator concept, and two member functions: begin() returns the start iterator of the sequence and end() returns the iterator referring to the past-the-end position of the sequence. The list class template example from the previous section can be extended as follows, though we leave the actual implementation of the iterator open.
template <class T> class list { void push_back( const T& t); // append t to list. typedef ... iterator; iterator begin(); iterator end(); };

Generic algorithmsarenotwrittenforaparticularcontainerclassinSTL,theyuseiteratorsinstead.Forexample,agenericcontainsfunction canbewrittentoworkforanymodelofaninputiterator.Itreturnstrueiffthevalueiscontainedinthevaluesoftherange [first,beyond).


template <class InputIterator, class T> bool contains( InputIterator first, InputIterator beyond, const T& value){ while ((first != beyond) && (*first != value)) ++first; return (first != beyond); } ThisgenericcontainsfunctioncanbeusedwithC-pointersreferringtoaC-array.RecallthatC-pointersareamodelforarandomaccess

iterator,whichismoregeneralthananinputiterator.Thefollowingexampledeclaresanarrayofahundredintegersandsearchesfora42.
int a[100]; // ... initialize elements of a. bool found = contains( a, a+100, 42);

Wecanalsosearchonlyapartofanarray.
bool in_first_half = contains( a, a+50, 42); bool in_third_quarter = contains( a+50, a+75, 42); Thisgenericcontainsfunctioncanalsobeusedwithourlistclasstemplateasillustratedinthefollowingexample: list<int> ls; // ... insert some elements into ls. bool found = contains( ls.begin(), ls.end(), 42);

Agenericcopyfunctioncopiesthevaluesofaniteratorrangetoasequencestartingwhereanotheriteratorpointsto.Thecopyfunction returnsaniteratorpointingtothepast-the-endpositionofthetargetsequenceaftercopying.
template <class InputIterator, class OutputIterator> OutputIterator copy( InputIterator first, InputIterator beyond, OutputIterator result){ while (first != beyond) *result++ = *first++; return result; }

Letscopy100elementsfromanarrayofintegerstoanotherarrayofintegers.
int a1[100]; int a2[100]; // ... initialize elements of a1. copy( a1, a1+100, a2);

Thecopyfunctioniswritingoverthealreadyexistingelementsina2.Ifwewanttocopythe100elementsintoalistthatisemptyatthe beginning,wecannotusethebegin()iteratorofthelist.Foranemptylistthebegin()iteratorisactuallyequaltotheend()iterator,which isnotdereferenceable.

file://C:\Users\rmanoj\Desktop\Untitled.htm

1/13/2010

Page 16 of 72

The STL provides in these cases small adapters that interface between the concepts. Here, the adapter is a model of an output iterator, and it uses a model of a container class, here the list, to append a new element to the end of this container class whenever an element is written to the iterator. We will see later on how this back_inserter adaptor is actually implemented. Here is the example how it is used with the copy function and the list class assuming we still have the array a1 at hand.
list<int> ls; copy( a1, a1+100, back_inserter(ls));

TherearealsoadapterstointerfacebetweenC++I/Ostreamsanditerators.Thefollowingexamplereadsintegersfromthestandardinput streamandwritesthemtothestandardoutputstream,eachintegerfollowedbyacarriagereturn"\n".Theistream_iteratorwiththe emptyparenthesisdenotesthepast-the-endpositionforthisrange,whichistheend-of-fileconditionforthestream.


copy( istream_iterator<int>(cin), istream_iterator<int>(), ostream_iterator<int>( cout, "\n"));

TheconceptsintheSTLandtheadaptorsformanextremelyflexibletoolkit.Mostadaptorsaresmallclassesandfunction.Ownadaptorsfor otherconceptsareeasytoadd.Thewholeismorethanthesumofitsparts.

A First Partial Implementation of an Iterator


Thestreamiteratoradaptorexamplemakesapoint:Streams,andranges,canbeinfinite.Fortechnicalreasons,thisideaworksbestwithinput iteratorsthatgeneratethesequenceonthefly,i.e.,theycomputethesequencefromasmallinternalstate.Afirstexamplewouldbean iteratortoaconstantvalue.
template <class T> class Const_value { T t; public: // Default Constructible ! Const_value() {} explicit Const_value( const T& s) : t(s) {} // Assignable by default. // Equality Comparable (not so easy what that should mean here) bool operator==( const Const_value<T>& cv) const { return ( this == &cv); } bool operator!=( const Const_value<T>& cv) const { return !(*this == cv); } // Trivial Iterator: const T& operator* () const { return t; } const T* operator->() const { return & operator*(); } // Input Iterator Const_value<T>& operator++() { return *this; } Const_value<T> operator++(int) { Const_value<T> tmp = *this; ++*this; return tmp; } };

Notethatoperator!=andoperator++(int)areimplementedintermsofothermemberfunctionsoftheiterator.Inthisexample,they areunnecessarilycomplicated.Butingeneral,onlyasmallsubsetofthememberfunctionsneedstobeimplementedforanewiterator,all othermemberfunctionsaregeneric.

Other examples for such simple input iterators are a counting iterator and a random number generator. Using the concept of lazy evaluation from functional programming languages we can also imagine iterators representing more complex and potentially infinite sequences, for example, the sequence of prime numbers. However, there is no point in copying an infinite sequence. Instead, we might be interested in a finite subsequence. Another generic function, copy_n solves this. Note that copy_n is not part of the C++ standard, but it is available in most implementations of the STL (or easy to write). (see also Const_value.C)
int a[100]; Const_value<int> cv( 42); copy_n( cv, 100, a); // fills a with 100 times 42.

Function Objects
Afunctionobjectbasicallyisaninstanceofaclasswiththeoperator()memberfunctionimplemented,suchthatacalltothismember functionoftheobjectlookslikeafunctioncall. Concept
Generator Unary Function

Refinement of
Assignable Assignable

Syntactic requirements functioncall,noarguments:Result operator()() functioncall,oneargument:Result operator()(Arg1) functioncall,twoarguments:Result operator()(Arg1, Arg2)

Binary Function Assignable

file://C:\Users\rmanoj\Desktop\Untitled.htm

1/13/2010

Page 17 of 72

Predicate

Unary Function resulttypeisbool

Binary Predicate Binary Function resulttypeisbool

Function objects are well suited as parameters for generic functions. A typical example would be the exchange of the equality comparison with a function object, which is currently hard coded as the operator== in the generic contains function from above. First, we define a function object equals that performs the same comparison.
template <class T> struct equals { bool operator()( const T& a, const T& b) { return a == b; } }; Wemodifytheiterator-basedgenericcontainsfunctionfromabove.ItneedsanadditionaltemplateparameterEqandtakesanadditional functionparametereqforabinaryfunctionobjectwhichisusedforthecomparison. template <class InputIterator, class T, class Eq> bool contains( InputIterator first, InputIterator beyond, const T& value, Eq eq ) { while ((first != beyond) && ( ! eq( *first, value))) ++first; return (first != beyond); } TheexampleusingC-arrayswiththecontainsfunctionneedsnowanadditionalargument--thefunctionobject.Theexpression equals<int>()callsthedefaultconstructorforthetemplateclassequals<int>fromabovewhichisafunctionobjectcomparingtwo

integersforequality.
int a[100]; // ... initialize elements of a. bool found = contains( a, a+100, 42, equals<int>());

Thenextsectionillustrateshowtheadditionalparameterofthecontainsfunctioncanbeautomaticallyselectedifthevaluetypeofthe iteratorisknown.C++allowstousealsosimplefunctionpointersasfunctionobjects.Theadvantageofobjectsisthattheycanhavean internalstate.Wecontinueourexampleofthecontainsfunctionanddefineacomparisonobjectthatistruewhentheabsolutevalueofthe differenceofitstwoargumentsissmallerthaneps.Theepsvalueisstoredinthefunctionobjectitself.Atconstructiontimeofthefunction objecttheactualvalueforepsisinitialized,inourexampletoone,sothatthecontainsfunctionwillalsoreturntrueifthevalues41or43do occurintherange.


template <class T> struct eps_equals { T epsilon; eps_equals( const T& eps) : epsilon(eps) {} bool operator()( const T& a, const T& b) { return (a-b <= epsilon) && (b-a <= epsilon); } }; bool found = contains( a, a+100, 42, eps_equals<int>(1));

Howaboutafunctionobjectthatcountsthenumberofcomparisonsneededasaside-effect?Hereitis:
template <class T> struct count_equals { size_t& count; count_equals( size_t& c) : count(c) {} bool operator()( const T& a, const T& b) { ++count; return a == b; } }; size_t counter = 0; bool found = contains( a, a+100, 42, count_equals<int>(counter)); // counter contains number of comparisons needed.

NotethatsincefunctionobjectsareusuallypassedbyvalueintheSTLwestoreareferencetoanexternalcounterandnotthecountervalue itselfinthefunctionobjects.

Iterator Traits
Iteratorsrefertoitemsofaparticularvaluetype.Algorithmsparameterizedwithiteratorsmightneedthevaluetypedirectly.Assumingthat iteratorsareimplementedasclassesthevaluetypecanbedefinedasalocaltypeoftheiterator,asinthefollowingexampleofaniterator referringtointegervalues.Thevaluetypecanbereferredtowiththeexpressioniterator_over_ints::value_type.
struct iterator_over_ints { typedef int value_type; // ... };

SinceaC-pointerisavaliditerator,thisapproachisnotsufficient.ThesolutionchosenfortheSTListheiteratortraitsclass,whichisaclass templateparameterizedwithaniterator:
template <class Iterator> struct iterator_traits { typedef typename Iterator::value_type // ... }; value_type;

Thevaluetypeoftheiteratorexampleclassabovecannowbeexpressedasiterator_traits< iterator_over_ints >::value_type. ForC-pointersaspecializedversionoftheiteratortraitsclassexists.


template <class T>

file://C:\Users\rmanoj\Desktop\Untitled.htm

1/13/2010

Page 18 of 72

struct iterator_traits<T*> { typedef T value_type; // ... };

NowthevaluetypeofaC-pointer,e.g.,toint,canbeexpressedasiterator_traits< int* >::value_type.Here,partial specialization isrequired.Theiteratortraitsclasscontainsalsodefinitionsaboutthedifference_type,theiterator_category,thepointertypeand thereferencetypeoftheiterator.

The example of the generic contains function with the function object from above can be made more convenient for the default use with a default initializer as follows: (see also contains.C)
template <class InputIterator, class T> bool contains( InputIterator first, InputIterator beyond, const T& value) { typedef typename iterator_traits<InputIterator>::value_type value_type; typedef equals<value_type> Equal; return contains( first, beyond, value, Equal()); } STLmakesuseoftraitsclassesinotherplacesaswell,forexample,char_traitstodefinetheequalitytestandotheroperationsfora charactertype.Inaddition,thischaractertraitsclassisusedasatemplateparameterforthebasic_stringclasstemplate,whichallowsthe

adaptionofthestringclasstodifferentcharactersets.

Implementing Adaptable Function Objects


Adaptablefunctionobjectsrequireinadditiontoregularfunctionobjectssomelocaltypesthatdescribetheresulttypeandtheargument types.Afunctionpointercanbeavalidmodelforafunctionobject,butitcannotbeavalidmodelofanadaptablefunctionobject. Concept
Adaptable Generator Adaptable Unary Function

Refinement of
Generator Unary Function

Syntactic requirements, model T


T::result_type T::result_type,T::argument_type T::result_type,T::first_argument_type, T::second_argument_type

Adaptable Binary Function Binary Function Adaptable Predicate Adaptable Binary Predicate Predicate,Adaptable Unary Function Binary Predicate,Adaptable Binary Function

Small helper classes help to define adaptable function objects easily. For example, our function object equals from above could be derived from std::binary_function to declare the appropriate types.
#include <functional> template <class T> struct equals : public std::binary_function<T,T,bool> { bool operator()( const T& a, const T& b) { return a == b; } }; Thedefinitionofbinary_functionintheSTLisasfollows: template <class Arg1, class Arg2, class Result> struct binary_function { typedef Arg1 first_argument_type; typedef Arg2 second_argument_type; typedef Result result_type; };

Adaptablefunctionobjectscanbeusedwithadaptorstocomposefunctionobjects.Theadaptorsneedtheannotatedtypeinformationto declareproperfunctionsignaturesetc.Anexamplesisthenegaterunary_negatethattakesanunarypredicateandisitselfamodelforan unarypredicate,butwithnegatedbooleanvalues.


template <class Predicate> class unary_negate : public unary_function< typename Predicate::argument_type, bool> { protected: Predicate pred; public: explicit unary_negate( const Predicate& x) : pred(x) {} bool operator()(const typename Predicate::argument_type& x) const { return ! pred(x); } };

Thefunctionadaptorsarepairedwithfunctiontemplatesforeasycreation.Theideaisthatthefunctiontemplatederivesthetypeforthe templateargumentautomatically(becauseofthematchingtypes).
template <class Predicate> inline unary_negate< Predicate> not1( const Predicate& pred) { return unary_negate< Predicate>( pred); }

Ashortprogramin[Stepanov95]makesuseofthisnegater.Theprogramcopiesallintegersfromcintocoutthatcannotbedividedbythe

file://C:\Users\rmanoj\Desktop\Untitled.htm

1/13/2010

Page 19 of 72

integerparametergiventotheprogram.(seealsoremove_if_divides.C)
int main( int argc, char** argv) { if ( argc != 2) throw( "usage: remove_if_divides integer\n"); remove_copy_if( istream_iterator<int>(cin), istream_iterator<int>(), ostream_iterator<int>(cout, "\n"), not1( bind2nd( modulus<int>(), atoi( argv[1])))); return 0; } Theotherfunctionobjectadaptorinthisexample,bind2nd,isagainasmallhelperfunctiontocreateanobjectoftypebinder2nd. template < class Operation, class Tp> inline binder2nd< Operation> bind2nd( const Operation& fn, const Tp& x) { typedef typename Operation::second_argument_type Arg2_type; return binder2nd< Operation>( fn, Arg2_type(x)); } Anobjectoftypebinder2ndstoresanadaptablebinaryfunctionobjectandavaluecompatiblewiththetypeofthesecondargumentofthe

adaptablebinaryfunctionobject.Theobjectitselfbehavesthenlikeanunaryfunctionobject.Wheneveritsoperatoriscalled,itreturnsthe valueofthebinaryfunctionobjectcalledwithitsargumentanditsinternallystoredvalueassecondargument.Thisadapterbindsavalueto thefreevariableofthesecondargumentofabinaryfunctionobject.Thereisasimilaradaptorcalledbinder1stthatbindsavaluetothefirst argument.Thisissimilartocurryingknowninfunctionalprogramminglanguages(itneedsmuchmorewritinginC++tomakeitwork,butthen itworks).So,thesearehigherorderfunctionobjects.


template <class Operation> class binder2nd : public unary_function< typename Operation::first_argument_type, typename Operation::result_type> { protected: Operation op; typename Operation::second_argument_type value; public: binder2nd( const Operation& x, const typename Operation::second_argument_type& y) : op(x), value(y) {} typename Operation::result_type operator()(const typename Operation::first_argument_type& x) const { return op(x, value); } };

Otherfunctionobjectadaptorsexistthatcancomposefunctionobjects,orencapsulatefunctionpointersandmemberfunctionpointersin adaptablefunctionobjects.

Implementation of the Iterator Adaptor back_inserter


Aclasstemplatethatisamodelofanoutputiterator.Itkeepsareferencetoacontainerclassasinternalstate.Eachtimeanexpressionfora back_insertiteratoriofthefrom*i = value;isevaluated,thevalueisappendedtothecontainerclassusingthepush_back()member function.
template <class Container> class back_insert_iterator { protected: Container* container; public: typedef Container typedef output_iterator_tag typedef void typedef void typedef void typedef void

container_type; iterator_category; value_type; difference_type; pointer; reference;

explicit back_insert_iterator(Container& x) : container(&x) {} back_insert_iterator<Container>& operator=(const typename Container::value_type& value) { container->push_back(value); return *this; } back_insert_iterator<Container>& operator*() { return *this; } back_insert_iterator<Container>& operator++() { return *this; } back_insert_iterator<Container>& operator++(int) { return *this; } };

Asmallhelperfunctiontemplateprovidesagaintheconveniencenottotypethetemplateargumentsexplicitly.
template <class Container> inline back_insert_iterator<Container> back_inserter(Container& x) { return back_insert_iterator<Container>(x); }

Hereisashortexampleofitsusewithalistclass.
list<int> ls; copy( a1, a1+100, back_inserter(ls));

file://C:\Users\rmanoj\Desktop\Untitled.htm

1/13/2010

Page 20 of 72

More Iterators
SeeIterator_identity.handIterator_identity.Cforanadaptorclassthattakesaniteratorandbehavesitselfexactlylikethisiterator.The exampleinIterator_base.handIterator_base.Cimplementsthesameadaptor,butbasedontheBarton-Nackmantrick

Function Dispatch using Iterator Category at Compile Time


Aniteratorbelongstoaspecificiteratorcategory.Thiscategorycanusedtoselectdifferentalgorithms.Forexamplethedifferencebetween twoiteratorscanbecomputedinconstanttimeforrandomaccessiterators,butcanonlybecomputedinlineartime(bycounting)forallother categories.

The C++ standard defines five empty classes to denote the different iterator categories. These types will be used as symbolic tags at compile time.
struct struct struct struct struct input_iterator_tag {}; output_iterator_tag {}; forward_iterator_tag : public input_iterator_tag {}; bidirectional_iterator_tag : public forward_iterator_tag {}; random_access_iterator_tag : public bidirectional_iterator_tag {}; Aniteratorisassumedtohavealocaltypeiterator_categorythatisdefinedtobeoneofthesetags. struct Some_iterator { typedef forward_iterator_tag iterator_category; // ... }; Thisiteratorcategoryisaccessedusingiteratortraits.Nowwecanimplementagenericdistancefunction(originalimplementationasitisin

theSTL):
template <class InputIterator> inline typename iterator_traits<InputIterator>::difference_type __distance( InputIterator first, InputIterator last, input_iterator_tag) { typename iterator_traits<InputIterator>::difference_type n = 0; while (first != last) ++first; ++n; return n; } template <class RandomAccessIterator> inline typename iterator_traits<RandomAccessIterator>::difference_type __distance( RandomAccessIterator first, RandomAccessIterator last, random_access_iterator_tag) { return last - first; } template <class InputIterator> inline typename iterator_traits<InputIterator>::difference_type distance( InputIterator first, InputIterator last) { typedef typename iterator_traits<InputIterator>::iterator_category Category; return __distance(first, last, Category()); }

Notehowtheclasshierarchyamongtheiteratortagsisusedtoreducethenumberofoverloadedfunctions__distancethatneedtobe implementedhere.Followingtherefinementrelationoftheiteratorconcepts,theforward_iterator_tagshouldbederivedalsofromthe output_iterator_tag.Obscurereasonsaboutmultiplederivationkeptthisderivationoutofthestandard.Ontheotherhand,this derivationisn'tlikelytosimplifyrealimplementationsanyway.

These tags are quite convenient to annotate symbolic information at compile time. However, there is a catch. An object has always non-zero size, even of an empty class. This is reasonable (the address identifies an object) and helps defining invariants about size, allocation, arrays, etc. However, if we derive from an empty class, like we do with function objects and binary_function<Arg1,Arg2,Result>, we would like to avoid any size penalties. In principle the compiler could perform this optimization, but, for example, g++ does not. The following program shows the effect.
#include <iostream> using namespace std; class A class B int }; class C int }; {}; : public A { i; { i;

int main() { cout << "size of A = " << sizeof(A) << endl; cout << "size of B = " << sizeof(B) << endl; cout << "size of C = " << sizeof(C) << endl; return 0;

file://C:\Users\rmanoj\Desktop\Untitled.htm

1/13/2010

Page 21 of 72

4. Design Strategies
Domain Design
Thedomainengineeringprocess(seeGenerativeProgramming)consistsofthreemaintasks:domainanalysis,domaindesign,anddomain implementation.Thepurposeofthesecondstep,domaindesign,istodevelopthearchitecture.ThedomainengineeringmethodDEMRAL [Czarnecki00]dividesdomaindesignintothreeactivities: 1. Identificationandspecificationoftheoverallimplementationarchitecture 2. Identificationandspecificationofdomain-specificlanguages(DSLs) 3. Specificationoftheconfigurationknowledge Itinvolvesthefollowingtasks: Scopedomainmodelforimplementation Identifypackages Developarchitecturesandidentifyimplementationcomponents IdentifyuserDSLs IdentifyinteractionsbetweenDSLs SpecifyDSLsandtheirtranslation

Domain Specific Languages


Adomain specific language(DSL)isaspecializedproblem-orientedlanguage.DSLsrangefromseparatespeciallanguagessuchasSQLto implicitlanguageextensionssuchasalibraryuserinterface.ConventionallibraryinterfacesasDSLsarerestrictedwithrespectto: syntacticandsemanticextensions domain-specificoptimizations domain-specificerrorchecking InC++,templatemetaprogrammingoffersatooltoaddresstheselimitation.

Generative programming emphasizes multiple modular, composable DSLs instead of single monolithic DSL. DEMRAL has two kinds of DSLs:
Configuration DSLs:Usedforspecifyingtheconfigurationofdatatypesandalgorithm. Expression DSLs:Usedforwritingexpressionsinvolvingthelibrarydatatypes.(seeExpressionTemplates) Czarnecki00]:
// specify matrix configurations using a configuration DSL typedef MATRIX_GENERATOR< matrix< double, rect<> > >::RET RectMatrixType; typedef MATRIX_GENERATOR< matrix< double, symm<> > >::RET SymmMatrixType; // matrix RectMatrixType R1(3, 3), R2(3, 3); SymmMatrixType S(3, 3); R1 = 1, 0, 3, 0, 1, 4, 2, 0, 1; S = 4, 4, 5, 4, 2, 6, 5, 6, 1; R2 = (R1 + S) * R1;

-->size=2width="100%"align=center>

Configuration DSLs
AuserofalibraryspecifiestheconfigurationofalibrarydatastructureoralgorithmusingaconfigurationDSL.Thespecificationisthen translatedintoaconcreteconfigurationexpressedinimplementation components configuration language(ICCL).

file://C:\Users\rmanoj\Desktop\Untitled.htm

1/13/2010

Page 22 of 72

DSL and ICCL serve different purposes. DSL is designed to provide a convenient interface for the user of the library. ICCL is designed to allow flexible, reusable implementation components. ICCL describes the full configuration, which can be complex, while DSL allows specification using different levels of detail:
nodetails(usedefaults) usageprofile(forexample,optimizeforspaceortime) directspecificationofcomponents user-providedimplementation HavingbothDSLandICCLseparatestheproblemspaceandthesolutionspace.Thisallowsindependentevolutionoftheusercodeandthe implementationcomponentcode.MostlibrariesdonotmakeaclearseparationbetweenDSLandICCL.ForexampleinSTL,configurationis usuallydirectlyspecifiedbytheuser.Thisseparationhasacentralroleingenerativeprogramming,wherethetranslationisperformedusing generators.

Policy-Based Class Design


Policy-basedclassdesign[Alexandrescu01]isacomponent-orientedarchitecturaldesigntechnique.Itdecomposesthefunctionalityofaclass intopolicies.Eachpolicycanhavemultipleimplementations,whicharecalledpolicy classes.Themainclassorhost classobtainsthe functionalityofapolicybygettingapolicyclassasatemplateargument.

As an example, let us define a policy for creating objects. Here are two implementations:
template <class> struct OpNewCreator { static T* create() { return new T; } }; template <class T> struct MallocCreator { static T* create() { void* buf = std::malloc(sizeof(T)); if (!buf) return 0; return new(buf) T; } };

Wehavealsoahostclassthatusesthepolicy.
template <class CreationPolicy = OpNewCreator<Widget> > class WidgetManager : public CreationPolicy { // ... }; NowtheuserisabletoselectthewayWidgetManagercreatesobjects. typedef WidgetManager<> DefaultWidgetManager; typedef WidgetManager< MallocCreator<Widget> > MallocWidgetManager;

Assuming WidgetManager always wants a creation policy for objects of type Widget, requiring the user to specify this is redundant and even dangerous. To avoid this, we can use template template parameters. This also allows the WidgetManager to create objects of other types using the same policy:

file://C:\Users\rmanoj\Desktop\Untitled.htm

1/13/2010

Page 23 of 72

// library code template <template <class> class CreationPolicy = OpNewCreator> class WidgetManager : public CreationPolicy<Widget> { // ... void doSomething() { Gadget* q = CreationPolicy<Gadget>().Create(); // ... } // ... }; //application code typedef WidgetManager<MallocCreator> MallocWidgetManager;

Enriched Policies
Acreationpolicyclassisrequiredtohaveamemberfunctioncreate,butitcanalsohaveadditionalfunctionality:
template <class T> struct PrototypeCreator { PrototypeCreator(T* p = 0) : prototype(p) {} T* create() { return prototype ? prototype->clone() : 0; } T* get_prototype() { return prototype; } void set_prototype(T* p) { prototype = p; } private: T* prototype; };

Thehostclassinheritsthisadditionalfunctionality,andtheusercantakeadvantageofthis:
typedef WidgetManager<PrototypeCreator> MyWidgetMgr; // ... Widget* p = ...; MyWidgetMgr mgr; mgr.set_prototype(p); //...

The"lazy"implicitinstantiationofmemberfunctionsofclasstemplatesenablesevenWidgetManageritselftousetheadditional functionality:
template <template <class> class CreationPolicy = OpNewCreator> class WidgetManager : public CreationPolicy<Widget> { // ... void switch_prototype(Widget* p) { CreationPolicy& myPolicy = *this; delete myPolicy.get_prototype(); myPolicy.set_prototype(p); } // ... }; UsercanstillcreateobjectsoftypeWidgetManager<OpNewCreator>eventhoughOpNewCreatordoesnothaveamemberfunctions get_prototypeandset_prototype.Acompilererrorwilloccuronlyiftheusertriestocallswitch_prototype.

Combining Policies
Thepowerpoliciesbecomesmoreapparentwhentherearemultiplepolicies.TheLokilibrary[Alexandrescu01]containsanimplementationof smartpointersasahostclasswithfourpolicies:
template < class T, template <class> class class template <class> class template <class> class > class SmartPtr; OwnershipPolicy ConversionPolicy CheckingPolicy StoragePolicy = = = = RefCounted, DisallowConversions, AssertCheck, DefaultSPStorage

EachpolicyhasmultipleimplementationsinLoki: Ownershippolicy:DeepCopy,RefCounted,RefCountedMT,COMRefCounted,RefLinked,DestructiveCopy,andNoCopy. Conversionpolicy:AllowConversionandDisallowConversion. Checkingpolicy:AssertCheck,AssertCheckStrict,RejectNullStatic,RejectNull,RejectNullStrict,andNoCheck. Storagepolicy:DefaultSPStorage,ArrayStorage,LockedStorage,andHeapStorage. Altogetherthisgives7x2x6x4=336combinationsusing1+7+2+6+4=20components.Thatis,336differentsmartpointerclasses!Furthermore, laterversionofthelibrarycouldaddnewpolicyclassesanduserscandefinetheirownpolicyclasses.

Named Template Arguments


In SmartPtr, all the policies have defaults; just writing SmartPtr<int> gives the default configuration. Overriding the default

file://C:\Users\rmanoj\Desktop\Untitled.htm

1/13/2010

Page 24 of 72

ownership policy is also simple: SmartPtr<int, NoCopy>. But if one wants to override the default storage policy, all the template arguments must be given. A more convenient configuration DSL would allow one to write, for example
SmartPtr<int, StoragePolicy_is<LockedStorage> > sp;

Atechniqueforimplementingsuchnamed template argumentsisdescribedin[Vandevoorde03,Section16.1].Letusconsiderasimple example:aBreadSlicerclasswiththreepolicyparametersPolicy1,Policy2andPolicy3.TheBreadSliceritselfisdefinedasfollows.


template < class PolicySetter1 = DefaultSetter, class PolicySetter2 = DefaultSetter, class PolicySetter2 = DefaultSetter > class BreadSlicer { typedef PolicySelector < PolicySetter1, PolicySetter2, PolicySetter3 > Policies; // use policies: Policies::P1 // ... Policies::P2 // ... Policies::P3 // ... }; HereBreadSlicerstemplateargumentsarepolicysettersinsteadofthepoliciesthemselves.Thepolicysetterscanbedefinedlikethis. struct DefaultPolicies { typedef DefaultPolicy1 P1; typedef DefaultPolicy2 P2; typedef DefaultPolicy3 P3; }; class DefaultSetter : virtual public DefaultPolicies {}; template struct Policy1_is : virtual public DefaultPolicies { typedef Policy P1; }; template struct Policy2_is : virtual public DefaultPolicies { typedef Policy P2; }; template struct Policy3_is : virtual public DefaultPolicies { typedef Policy P3; }; Finally,thePolicySelectorextractstheactualpoliciesfromthepolicysetters. template <class Base, int D> struct Discriminator : public Base {}; template < class Setter1, class Setter2, class Setter3 > class PolicySelector : public Discriminator<Setter1,1>, public Discriminator<Setter2,2> public Discriminator<Setter3,3> {}; ThepurposeoftheDiscriminatortemplateistoavoidanerrorwhentwoormoreofthesettersarethesameclass(DefaultSetter).

Let us see what happens when we override the default for the second policy: BreadSlicer<Policy2_is<CustomPolicy> >. This leads to the definition of PolicySelector<Policy2_is<CustomPolicy>,DefaultSetter,DefaultSetter>, whose structure looks like this:

Note that there is only one DefaultPolicies object, because DefaultPolicies is a virtual base class of the setters. This is important to avoid defining P1 and P2 multiple times, which would cause a compiler error due to ambiguity. P2 is defined twice but the one in Policy2_is<CustomPolicy> dominates the one in DefaultPolicies, because Policy2_is<CustomPolicy> is the more derived class. This is exactly what we want.

GenVoca Architecture

file://C:\Users\rmanoj\Desktop\Untitled.htm

1/13/2010

Page 25 of 72

GenVocaisasoftwarearchitecturemodelthatissimilartopolicy-basedarchitecturesinthatfinaltypesarebuildfromcomponents,butthe componentsareorganizeddifferently.InGenVocathecomponentsarenottypicallypoliciesthatarepluggedintoimplementsomepartofthe functionalitybutwrappers(layers)ontopofamorebasiccomponentsaddingfunctionalitytoit.

The key GenVoca terms are layers and realms. A layer represents a component and a realm is a standardized interface exported by components. A layer is said to belong to a realm if it exports the required interface. A layer can also import one or more realms. Layers and realms correspond roughly to models and concepts in STL parlance. A specific GenVoca model can be represented by a GenVoca grammar. For example, in the grammar
R : A | B[R] S : C[R] | D[S] | E[R,S] RandSarerealms,andA,B,C,DandEarelayers.AGenVocagrammardefinesanICCL.Possibleconfigurationsofthemodelcanbedescribed byGenVocaexpressions.Forexample,validconfigurationsoftherealmSintheabovegrammarinclude:

In an important class of GenVoca models, called stacking models, layers are organized into a hierarchical set of layer categories. Each layer in a category belongs to the same realm and, except for the bottom category, takes exactly one parameter which must belong to the realm of the layer category below. Then valid configurations include exactly one layer from each category. As an extension, parameters of the same realm and optional layer categories can be allowed. For example, A has a parameter of the same realm and R3 is an optional category in the following stacking model:
R1 R2 R3 R4 : : : : A[R1] | B[R2] | C[R2] D[R3] F[R4] | G[R4] | R4 H | I

GenVoca Example: List


AsexampleofaGenVoca,letusdesignanarchitectureforasimplelistwiththefeaturediagram:

Moredetailsonthisexamplecanbefoundin[Czarnecki00,Chapter12].

Designing a GenVoca architecture consists of the following steps.


1. Identify the main responsibilities in the feature diagram.Inourexample,theresponsibilitiesinclude: storageofelements copyingofelements destroyingelements dynamictypechecking(toensuremonomorphism) lengthcounter tracing 2. Enumerate component categories and components per category.Thereisacomponentcategoryforeachresponsibilityoftheprevious stepandacomponentforeachalternativeimplementationoftheresponsibility.Intheexample,thecategoriesandcomponentsare: BasicList: PtrList Copier: PolymorphicCopier, MonomorphicCopier, EmptyCopier Destroyer: ElementDestroyer, EmptyDestroyer

file://C:\Users\rmanoj\Desktop\Untitled.htm

1/13/2010

Page 26 of 72

TypeChecker: DynamicTypeChecker, EmptyTypeChecker Counter: LengthList Tracing: TracedList

3. Identify ``use'' dependencies between component categories.Thedependeciesoftheexamplearegivenbythefollowingfigure:

4. Sort the categories into a layered architecture.A``user''componentcategoryisplacedinahigherlayercategorythanthe``used''one. Fromtheabovefigure,wegetfourlayercategories.Tracing,CounterandBasicListeachformtheirownlayercategories.The componentcategoriesthatdonotdependonothercategoriesaregroupedintothebottomConfigurationRepository(Configfor short)layercategory.TracingandCounterareoptionallayercategories. 5. Write down the GenVoca grammar.
6. List 7. OptCounterList 8. BasicList 9. Config 10. Copier 11. Destroyer 12. TypeChecker 13. ElementType 14. LengthType : : : : : : : : : TracedList[OptCounterList] | OptCounterList LengthList[BasicList] | BasicList PtrList[Config] PolymorphicCopier | MonomorphicCopier | EmptyCopier ElementDestroyer | EmptyDestroyer DynamicTypeChecker | EmptyTypeChecker [ElementType] int | short | long | ...

Notehowthereisnoone-to-onecorrespondencebetweenthefeaturediagramandtheGenVocagrammar.Forexample,Copierisaffected bybothOwnershipandMorphologyfeatures.

Implementing a GenVoca Architecture


WewillnowimplementthelistdescribedbytheaboveGenVocagrammar.Weneedtoimplementthefollowingcomponents:
TracedList LengthList PtrList PolymorphicCopier MonomorphicCopier EmptyCopier ElementDestroyer EmptyDestroyer DynamicTypeChecker EmptyTypeChecker

Thefullimplementationofthecomponentsisinlist_components.h.(Seealsolist_generator.handlist_example.C.)

A user can define a particular configuration by writing a configuration repository, for example:
struct TracedIntListConfig { typedef int typedef const ElementType typedef MonomorphicCopier<ElementType> typedef ElementDestroyer<ElementType> typedef DynamicTypeChecker<ElementType> ElementType; ElementArgumentType; Copier; Destroyer; TypeChecker;

typedef TracedList<PtrList<TracedIntlListConfig> > FinalListType; }; typedef TracedIntListConfig::FinalListType TracedIntList;

Notethecircularityinthedefinition.Theconfigurationrepositoryistheinnermost(bottom)layerofthelistbutcontainsthefinallisttypeasits member.Thisisnecessary,becausethehigherlayersneedthefinallisttype.ThetypeElementArgumentTypeisusedastheargumenttype wheninsertingelements.ItiseitherElementTypeorconst ElementTypedependingontheconfiguration.LengthTypeisnotdefined here,sincethislistconfigurationhasnolengthcounter.

Let us then implement the components. This is a very bare-bones implementation concentrating on the architectural aspects. We start with the basic list PtrList:
template <class Config_> class PtrList { public: // export Config typedef Config_ Config;

file://C:\Users\rmanoj\Desktop\Untitled.htm

1/13/2010

Page 27 of 72

private: // retrieve the needed types from the repository typedef typename Config::ElementType ElementType; typedef typename Config::ElementArgumentType ElementArgumentType; typedef typename Config::Copier Copier; typedef typename Config::Destroyer Destroyer; typedef typename Config::TypeChecker TypeChecker; typedef typename Config::FinalListType FinalListType; // data members ElementType* head_; FinalListType* tail_; public: PtrList (ElementArgumentType& h, FinalListType *t = 0) : head_(0), tail_(t) { set_head(h); } ~PtrList() { Destroyer::destroy(head_); } void set_head(ElementArgumentType& h) { TypeChecker::check(h); head_ = Copier::copy(h); } ElementType& head() { return *head_; } void set_tail(FinalListType *t) { tail_ = t; } FinalListType* tail() const { return tail_; } };

// note: not PtrList* but FinalListType*

Thisprimitivesinglylinkedlistcouldbeusedasfollows(seelist_example.Cformore):
template <class List> void print_list(List* l) { std::cout << "[ "; for ( ; l; l = l->tail() ) std::cout << l->head() << " "; std::cout << "]\n"; } template <class List> void push_front(typename List::ElementArgumentType& e, List*& l) { l = new List(e, l); } int main() { typedef ListConfig::FinalListType List; List* ls = 0; push_front(1,ls); push_front(2,ls); push_front(3,ls); print_list(ls); // prints "3 2 1" } ThePtrListdelegatessomeoftheworktoothercomponents.Thewayelementsarecopied(ornot)isdeterminedbytheCopierwhichis

oneofthefollowing:
template <class ElementType> struct MonomorphicCopier { static ElementType* copy(const ElementType& e) { return new ElementType(e); } }; template <class ElementType> struct PolymorphicCopier { static ElementType* copy(const ElementType& e) { return e.clone(); // polymorphic copy using } // virtual member function clone() }; template <class ElementType> struct EmptyCopier { static ElementType* copy(ElementType& e) { return &e; // no copy } };

// note: not const

Thecomponentsforelementdestructionandtypecheckingareevensimpler:
template <class ElementType> struct ElementDestroyer { static void destroy(ElementType* e) { delete e; } }; template <class ElementType> struct EmptyDestroyer {

file://C:\Users\rmanoj\Desktop\Untitled.htm

1/13/2010

Page 28 of 72

static void destroy(ElementType* e) {} // do nothing }; template <class ElementType> struct DynamicTypeChecker { static void check(const ElementType& e) { assert(typeid(e)==typeid(ElementType)); } }; template <class ElementType> struct EmptyTypeChecker { static void check(const ElementType& e) {} };

Finally,thehigherlayersareimplementedasinheritance-basedwrappers.OnlyLengthListingivenhere;TracedListissimilarandcanbe foundinlist_components.h.
template <class BasicList> class LengthList : public BasicList { public: // export config typedef typename BasicList::Config Config; private: // retrieve the needed types from the repository typedef typename Config::ElementType ElementType; typedef typename Config::ElementArgumentType ElementArgumentType; typedef typename Config::LengthType LengthType; typedef typename Config::FinalListType FinalListType; LengthType length_; LengthType compute_length() const { return tail() ? tail()->length()+1 : 1; } public: LengthList (ElementArgumentType& h, FinalListType *t = 0) : BasicList(h, t) { length_ = compute_length(); } void set_tail(FinalListType *t) { BasicList::set_tail(t); length_ = compute_length(); } LengthType length() const { return length_; } };

Generators
Thelistimplementationdescribedabovedefinesthecomponentsbutcontainslittleconfigurationinformation.Thismakesthecomponents simpleflexibleandreusable,butleavesasignificantpartofthefinallistdefinitiontothewriteroftheconfigurationrepository.Thisistoo tediousanderror-pronetasktobeleftfortheuser.Forexample,thecombinationofMonomorphicCopierandEmptyDestroyerwould likelyleadtomemoryleaks.Wewillnextwritealist generatortoautomatetheconfiguration.

A generator takes a possibly incomplete requirements specification written in a more convenient configuration DSL and produces the finished type. In general, a configuration generator performs the following tasks:
completethespecification(computedefaults) checkthatthespecificationisvalid assemblethecomponentsintothefinishedtype. Inourexample,theconfigurationDSLdoesnotallowinvalidspecifications,andtheonlydefaultsareprovidedbydefaulttemplatearguments.

First, we need to define the configuration DSL for specifying list configurations. It is based on the features in the feature diagram rather than the implementation components. We will represent the features using enumeration types:
enum enum enum enum Ownership {ext_ref, own_ref, cp}; Morphology {mono, poly}; CounterFlag {with_counter, no_counter}; TracingFlag {with_tracing, no_tracing};

Thegeneratortakesthesetoffeaturesastemplatearguments,translatesthespecificationintoaconfigurationrepository(configurationDSL toICCLtranslation),andproducesthefinallisttypeasresult.Thegeneratordoesthetranslationatcompiletime,andusesahelper metafunctionIFtochoosetypesbasedonbooleanconstants(seetemplatemetaprogrammingformoreaboutmetafunctions):


template <bool condition, class Then, class Else> struct IF {

file://C:\Users\rmanoj\Desktop\Untitled.htm

1/13/2010

Page 29 of 72

typedef Then RET; }; template <class Then, class Else> struct IF<false, Then, Else> { typedef Else RET; };

Thegeneratoritselfishere(andinlist_generator.h).
template < class ElementType_, Ownership ownership = Morphology morphology = CounterFlag counter_flag = TracingFlag tracing_flag = class LengthType_ = > class LIST_GENERATOR { public: cp, mono, no_counter, no_tracing, int

// forward declaration of the configuration repository struct Config; private: // define the constants used for type selection enum { is_copy = ownership==cp, is_own_ref = ownership==own_ref, is_mono = morphology==mono, has_counter = counter_flag==with_counter, does_tracing = tracing_flag==with_tracing }; // select the components typedef typename IF<is_copy || is_own_ref, ElementDestroyer<ElementType_>, EmptyDestroyer<ElementType_> >::RET Destroyer_; typedef typename IF<is_mono, DynamicTypeChecker<ElementType_>, EmptyTypeChecker<ElementType_> >::RET TypeChecker_; typedef typename IF<is_copy, typename IF<is_mono, MonomorphicCopier<ElementType_>, PolymorphicCopier<ElementType_> >::RET, EmptyCopier<ElementType_> >::RET Copier_; typedef typename IF<is_copy, const ElementType_, ElementType_ >::RET ElementArgumentType_; // define the list type typedef PtrList<Config> BasicList; typedef typename IF<has_counter, LengthList<BasicList>, BasicList >::RET OptLengthList; typedef typename IF<does_tracing, TracedList<OptLengthList>, OptLengthList >::RET List; public: // return the final list type typedef List RET; // define the configuration repository struct Config { typedef ElementType_ ElementType;

file://C:\Users\rmanoj\Desktop\Untitled.htm

1/13/2010

Page 30 of 72

typedef typedef typedef typedef typedef

ElementArgumentType_ Copier_ Destroyer_ TypeChecker_ LengthType_

ElementArgumentType; Copier; Destroyer; TypeChecker; LengthType; FinalListType;

typedef RET }; };

NowwecanspecifyalistconfigurationTracedIntListwesawearlierasfollows:
typedef LIST_GENERATOR<int,cp,mono,no_counter,with_tracing>::RET TracedIntList;

Ifnotracingwasrequired,wecouldsimplywrite
typedef LIST_GENERATOR<int>::RET IntList;

More examples can be found in list_example.C.

5. Advanced C++ Programming


Introduction
C++providesmanywaysofabstraction,waystoshieldtheuserfromimplementationdetails.Wewilldiscussseveralimportantconceptsinthis chapter:libraryinitialization,constcorrectness,breakingcyclictypedependencieswithtemplates,proxyclasses,variessmartpointers,and doubledispatch,atechniquetomakeafunction`virtual'fortwoargumentssimultaniously.

However, it is useful to remember that all the protections in C++ require the cooperation from the user. John Lakos describes in [Lakos96] the extreme measures a developer team chose when the library they were using turned out to be too restrictive:
#define private public #define protected public #define class struct

Thestandarddoesnotguaranteethatthisreallyworks,butitisprettyeffectiveandworksprobablywithmostcompilers.Thedeveloperswere wellawareoftheirsin,buttheyweredesperate.

We can distinguish between to kinds of protection a design can provide: protection against Murphy, and protection against Macchiavelly. Murphy describes an user that makes occasionally mistakes, while Macchiavelli describes an user that willingly tries to get around the protection mechanism. Protection against Macchiavelli in C++ is almost impossible, as C-style type casts and the example above illustrates. An effective but expensive solution would be opaque pointers and a link library manipulating the opaque pointer, where the sources of the underlying data are not published. Here, we discuss usually solutions that protect against Murphy.

Automatic Library Initialization and Housekeeping


In C
Initializersofglobalandstaticvariablesareautomatic.ButnofunctioncallcanbetriggeredautomaticallyinC,evennonewiththeCpreprocessor.Librarieshavetobeinitializedexplicitly,iftheinitializationisn'ttrivial.

Depending on the C runtime library, it might be possible to register with atexit() or on_exit() during initialization a callback function that will be called upon the exit of the main function. Otherwise, housekeeping has also to be implemented as an explicit function call. (see also housekeeping.c)

In C++
Weuseastaticmembervariable.Itsdefaultconstructorinitializesthelibrary,itsdestructorperformshousekeeping.However,anexplicit housekeepingfunctionmightbeappropriatetoavoidtheuncertaintiesaboutwhenthedestructorgetsfinallycalled.Theonlyrestrictionof thissolutionisthattheinitializationcannotrelyontheinitializationofstaticmembervariablesofotherclasses.Theorderofinitializationfor non-localstaticobjectsisunspecified.Findingafeasibleorderwouldbetoohard,infact,Halting-Problemequivalent[Item47,Meyers97](this referencedescribesalsohowtogetaroundthisrestrictionwithlocalstaticvariablesinglobalfunctions).Note,C++initializesalwaysnon-local staticobjects,eveniftheyarenotused.Buttheyhavetobeinacompilationunitthatactuallygetslinkedtotheexecutable.Thuswewritea headerfilewiththefollowingdeclarationandincludeitineachheaderthatreliesonthelibrarybeinginitialized.Thestaticmembervariable countcountsnowhowmanydifferentcompilationunitsareinitialized.Thelibraryisinitializedforthefirstcompilationunit,and housekeepingisperformedoncethelastcompilationunitdestructsthestaticinit_var(seealso[Page640,Stroustrup97]forthissolution).
#ifndef INIT_H #define INIT_H class Init { static unsigned int count; public: Init(); ~Init();

file://C:\Users\rmanoj\Desktop\Untitled.htm

1/13/2010

Page 31 of 72

}; // Trigger constructor and destructor calls in each compilation unit. // Unnamed namespace can be used to avoid name collisions. namespace { static Init _init_var; }; #endif // INIT_H //

Thereisacatchwiththissolutionifwehaveatemplatelibrarythatdoesnothaveanyobjectfilesforlinking--weneedtolinkwithanobject filethatcontainsthedefinitionofthestaticmembervariablecountandthisonemustonlyexistinonecompilationunit.Weimplementalso theconstructorandthedestructortomaintainthecounterandtoperformtheinitializationandhousekeeping.(seealsoEX/Init.handInit.Cfor afullimplementationinthelibraryexamplelib/)


unsigned int EX_Init::count = 0; Init::Init() { // default constructor if ( 0 == count++) { // perform initialization } } Init::~Init() { // destructor if ( 0 == --count) { // perform housekeeping } }

Foratemplatelibrarywhichconsistsonlyofheaderfiles,wecanbasicallyusethesameclass,butwemakeitaclasstemplatewithadummy templateparameterwhichallowsustomovethestaticmembervariabledefinitionfromtheobjectfileintotheheaderfile.(see EX/Template_init.hinthelibraryexamplelib/).\

Const Correctness
(Seealso[Item21and29,Meyers97].)

Const Declarations in C and C++


Aconstdeclarationofavariableforbidschangesofthevariableafteritsinitialization.
const int i = 5; // i = 6; // violates const declaration

Apointercanbedeclaredconstaswell,thusthevalueofthepointercannotchange,butthevalueitreferstocanchange.Aswell,apointer canbedeclaredtopointtoaconstantvalue.Andbothcanbecombined.Hereisthetableofallfourcombinations:
the pointer, the data it refers to --------------------------------------int* p; non-const non-const int* const q; const non-const const int* r; non-const const const int* const s; const const Ireadthesedeclarationsfromtheinsideout.Forexample,forconst int* const qIstartwithqthevariable.constmakesitconstant.* makesitapointer,thusaconstantpointer.intmakesitaconstantpointerreferringtoavalueoftypeint,andfinallytheleftconstdeclares thevalueoftypeinttobeconstantaswell. // // // // // //

Member variables in classes can be declared constant as well. However, they can only be initialized with the constructor initializer syntax, not in the constructor call. An example:
struct A { const int i; A() : i(42) {} };

Make Temporary Return Objects in C++ Classes Const


MemberfunctionsofclassesinC++haveahiddenparameterthis.ForaclassC,thisparameterisoftypeT* constifthememberfunction isdeclarednon-const,oritisoftypeconst T* constifthememberfunctionisdeclaredconst.
struct C { void foo(); // hidden parameter: T* const this; void bar() const; // hidden parameter: const T* const this; }; Forbuilt-indatatypesCandC++distinguishbetweenl-valuesandr-values.L-valuescanbeusedfortheleftsideofanassignment,theyare

non-const.R-valuescannotbeusedfortheleftsideofanassignment.Theycanonlybeusedfortherightsideofanassignment.Theyare const.Forexamplethepost-incrementoperatorrequiresanl-value,butisitselfanr-value.Thus,wecannotwrite:
int i; i++ ++; // second ++ forbidden!

ForclassesinC++weneedtomodelthisbehaviorexplicitlyusingconstdeclarations.Considerthefollowingclass:
struct A { A operator++ (int); }; // the post increment operator

Now,letstry:

file://C:\Users\rmanoj\Desktop\Untitled.htm

1/13/2010

Page 32 of 72

A a; a++++;

Fine,thatworks.Itworks,becausea++returnsatemporaryobjectoftypeA.Butitprobablydoesnotwhatonewouldexpect.Sincethe second++worksonatemporaryobject,aitselfgetsonlyincrementedonce.Wecanforbidthesecondincrementexplicitlybymakingthe returntype,thetypeofthetemporaryobject,const.Thisshouldbeconsideredforallsimilartemporaryreturntypes.


struct A { const A operator++ (int); }; // the post increment operator

Const Correctness and C++ Classes


Givenanobjectofaclassisdeclaredconst,thecompilerguaranteesbitwise constness,i.e.,noneofitsnon-staticmembervariablescanchange itsvalue.Ontheotherhand,auserofaclassexpectsconceptual constnesswhichmeansthatthevalueofanobjectcannotchangeits observablestateifitisdeclaredconst.

Bitwise constness and conceptual constness are not the same. Two reasons: internal, not observable variables, and pointers. There might be internal variables that can change but that cannot be observed. An example would be a string class that maintains a cache of the string length. The status of the cache cannot be observed from the outside (except in the runtime difference).
class string { char* s; size_t l; bool valid; public: string() : s(0), l(0), valid(true) {} size_t length() const; // const, does not change string conceptually ... // some more functions modifying s and setting valid to false }; size_t string::length() const { if ( ! valid) { l = strlen(s); // error, violates bitwise constness checked by the compiler! valid = true; // error, violates bitwise constness checked by the compiler! } return l; } Thenewkeywordmutablesolvestheproblemhere.Itcanbeappliedtoamembervariabletocancelouttheconstdeclaration.Changingthe

classdefinitionasfollows,theexamplefromaboveworks.
class string { char* s; mutable size_t l; mutable bool valid; public: string() : s(0), l(0), valid(true) {} size_t length() const; // const, does not change string conceptually ... // some more functions modifying s and setting valid to false };

Thesecondproblemofbitwiseconstnessoccurswithpointersandreferences.Bitwiseconstnessofapointersaysthatthepointercannot change,butthereferencedvaluecanchange.However,ifthereferencedvalueisconsideredtobepartoftheobservablestate,thereferenced valueshouldbeconsideredconstantaswell.Intheexampleofthestringclass,thepointerscannotchange,butthereferencedcharacter arraycouldbechanged.Theimplementorofthestringclassexplicitlyhastotakecare(andcantakecare)thatthiscannothappen.Thekeyis thattheclassneverexposes,i.e.,returns,anon-constpointerornon-constreferencetothearrayelements.Twoexamples:thearrayindex operatortoasinglecharacterelementinthearray,andamemberfunctionreturningtherawcharacterpointer.Notethatwewanttokeepthe fullfunctionalityforthenon-constcaseaswell.Weimplementbothmemberfunctionstwice,oncefortheconstcase,andonceforthenonconstcase.(seealsostring.C)


class string { char* s; mutable size_t l; mutable bool valid; public: string() : s(0), l(0), valid(true) {} char& operator[]( int idx) { valid = false; return s[idx];} const char& operator[]( int idx) const { return s[idx];} char* & get_pointer() { valid = false; return s; } const char* get_pointer() const { return s; } ... // some more functions }; Giventhisstringclass,wecanuseitsafelywithoutbreakingconstcorrectness. int main() { char buf[6] = "Hallo"; // German string s; s.get_pointer() = buf; assert( s[1] == 'a'); s[1] = 'e'; // Now it's in English assert( s[1] == 'e'); // get a const reference to the same string: const string& r = s;

file://C:\Users\rmanoj\Desktop\Untitled.htm

1/13/2010

Page 33 of 72

// r.get_pointer() = "Salute"; // does not work with const char *, r-value assert( r[1] == 'e'); // r[1] = 'a'; // no, does not work with const char & }

Proxy Classes
Adynamictwo-dimensionalarrayofintegerscouldbedeclaredinC++asfollows:
class Array2D { public: Array2D( int dim1, int dim2); // ... };

Ofcourse,inaprogramwewouldlikeusethearraysimilartothebuiltin(static)two-dimensionalarraysandaccessanelementasfollows:
int main() { Array2D a(5,10); // ... int i = a[2][8]; } However,thereisnooperator[][]inC++.Instead,wecanimplementoperator[]toreturnconceptuallyaone-dimensionalarray,where wecanapplyoperator[]againtoretrievetheelement. class Array1D { public: Array1D( int dim); int operator[](int i); // ... }; class Array2D { public: Array2D( int dim1, int dim2); Array1D& operator[](int i); // ... }; TheintermediateclassArray1Discalledproxy class,alsoknownassurrogate[Item30,Meyers97].Conceptually,itrepresentsaone-

dimensionalarray,butinthisapplicationwesurelydonotwanttocopytheelementstoactuallycreateaone-dimensionalarray.Theproxy classwilljustbehaveasifitisanone-dimensionalarrayandinternallyitwilluseapointertothetwo-dimensionalarraytoimplementits operations.

Another typical example for proxy classes is to distinguish between read and write access. The problem appears with string classes and their operator[] assuming the string class uses reference counting with 'copy-on-write' strategy.
struct string { const char& operator[](size_t index) const; char& operator[](size_t index); // ... }; int main() { string s = "Hello!"; char c = s[3]; s[5] = 'a'; } Sincesisnotdeclaredconstant,thenon-constindexoperatorwillbeusedforbothusesoftheoperator[].Sincethestringcouldbe modifiedthroughthisoperatoranditcannotseethattheresultofoperator[]appearsontherightsideoftheassignmentinthefirst

example,thestringclassmustmaketheconservativeassumptionthatitwillbechanged.Wecandeferthisdecisionbyintroducingaproxyfor thereturntypeofoperator[]:
struct string { const char& operator[] const; Proxy operator[]; // ... }

Theproxydistinguishestwocases:automaticconversiontoacharacterr-value,whichdoesnotchangethestring,suchasinthefirstexample above,ortheassignmentofacharactertotheproxy,whichcorrespondstoanl-valueassignment,suchasinthesecondexampleabove.The proxycontainsareferencetothestringitbelongsto:


class Proxy { string& str; int index; public: Proxy( string& s, int i) : str(s), index(i) {} // l-value uses, manipulate str if necessary Proxy& operator= ( const Proxy& rhs); Proxy& operator= ( char c); // r-value uses (str does not change) (automatic conversion to char) operator char() const; }

Notethattheclientcodedoesnotchange.Clientscanpretendthatoperator[]returnsacharinmostcases.

file://C:\Users\rmanoj\Desktop\Untitled.htm

1/13/2010

Page 34 of 72

Limitations pop up for other l-value uses of the proxy than assignment. For example, taking the address of the result of operator[], or the operators +=, -=, *= etc. However, all these operators can be implemented to behave correctly. But in case we would deal with a class with member functions instead of a builtin type, we should also consider the distinction between constant and non-constant member functions. They have to be implemented in the Proxy as well, which changes the problem of writing a proxy from a moderate sized finite set of operators to a potentially infinite set of member functions for a general proxy for arbitrary types. Another limitation is that proxies rely on an automatic conversion and the compiler can only use one automatic conversion to resolve expressions. The compiler cannot compose automatic conversions. Thus, some expressions that work without proxies do not work with proxies. We have already seen the use of a proxy in the STL when implementing back_insert_iterator (see this Section). The is an output iterator and we use a proxy as a return type of operator*. Assigning a value to the proxy writes it to the output iterator, i.e., appends it to the underlying container in this case. Note that in this example the proxy class is actually the back_insert_iterator itself (for no good reason except to write less code).
back_insert_iterator

Smart Pointers
Asmartpointerisanobjectthatbehavesmuchlikeapointer,buthassomeadditional``smart''functionality.Smartpointerclassesdiffer greatlyinwhatadditionalfunctionalitytheyprovideandhowcloselytheymimicbuilt-inpointers.(Formoreinformationonsmartpointers,see [Alexandrescu01,Chapter7]orBoostsmartpointerdocumentation.)

Smart pointers usually point to an object in dynamic memory and own it, that is, they are responsible of destroying and deleting the object when appropriate. This is the biggest deficiency of built-in pointers:
void f() { int* p = new int(42); // do something delete p; }

Thislooksfineatfirst,butitcanleadtoamemoryleakifthe``dosomething''partofthecodethrowsanexception(seeExceptionSafety)or containssomethinglike:
if (done) return;

Anothercontext,wherethefailureofabuilt-inpointertodeleteitspointeeisproblematic,isacontainerofpointers(seebelow).

Perhaps the most fundamental difference between various smart pointers is how they deal with copying. Consider:
void f() { SmartPtr p(new int(42)); SmartPtr q = p; }

Themostobviousimplementationwouldleadtodeletingthesameobjecttwice,whichisnotacceptable.Thisisabigproblem,because copyingoccursinmany,notalwaysobviousplacessuchaspassinganargumentbyvalue,returningbyvalue,andinsertingtoastandard container.Thefollowingthreesectionsdisplayfourdifferentapproachestocopyingsmartpointers: destructivecopy(move) nocopyingallowed (polymorphic)deepcopy referencecounting

Smart Pointers: std::auto_ptr<T>


Thestandardlibraryprovidesonesmartpointertemplate,auto_ptr.Itismeantasareplacementforbuilt-inpointersfor: holdingdynamicallyallocatedobjects passingownershipofdynamicallyallocatedobjectsintoandoutoffunctions Itisnotmeantforcontainersofpointers.

The auto_ptr solves the copying problem with destructive copy (or move) semantics. In a copy operation, the new pointer obtains the ownership and the old pointer becomes a null pointer.
#include <memory> using namespace std; auto_ptr<int> source() { return auto_ptr<int>( new int(42)); } void sink( auto_ptr<int> pt) {}

file://C:\Users\rmanoj\Desktop\Untitled.htm

1/13/2010

Page 35 of 72

int main() { // these are legal and safe expressions with auto_ptr sink( source()); auto_ptr<int> pt = source(); sink( pt); pt = source(); auto_ptr<int> pt2 = pt; // but this is not ++*pt; // error: dereference of a null pointer }

Howaboutthefollowingtwolines?
std::vector< auto_ptr<int> > vec; std::sort( vec.begin(), vec.end());

Itdepends,butmostlikelyitdoesnotworkasexpected.Thereasonisthattheunusualcopysemanticsofauto_ptr<T>(modifiestherighthandside)doesnotcomplywiththerequirementsforthetemplateparametersofSTLcontainerclassesandalgorithms.

How about the following line?


const auto_ptr<int> ptr( new int(42));

Thatisatrulyconstpointer.Itcannotevenbecopiedtoanotherconst auto_ptr<int>.Thisrepresentsanotherapproachtocopying:no copying allowed.However,thevalueitreferstocanbechanged.Thisvaluecouldbedeclaredconstantaswell,e.g.,const auto_ptr<const int>.(Seeauto_ptr.Cforthefullexample.)

Smart Pointers: Polymorphic Deep Copy


Asauto_ptrisnotsuitableforcontainers,letustakealookatasmartpointerthatis.Thissmartpointerhelpsinmanagingobjectsofaclass hierarchyinacontainerclass[Chapter5,Koenig96].Weuseasmallhierarchyofshapesasexample.
struct Shape { virtual Shape* clone() = 0; virtual ~Shape() {} }; struct Circle : public Shape { virtual Circle* clone(); }; struct Square : public Shape { virtual Square* clone() = 0; };

ContainerclassesintheSTLstoreobjectsbyvalue.Thus,storingobjectsoftypeShapewouldn'tallowustostoreobjectsoftypeCircle,they wouldbeslicedtotypeShapewhenwetrytostoretheminthecontainer.

Instead, we can store pointers to Shape in the container. With the lack of ownership of plain built-in pointers, we will use a smart pointer. For copying, we use deep-copy semantics: when a pointer is copied, so is the object it points to. Here, the virtual clone() function is provided for performing a polymorphic copy.
class ShapePtr { Shape* shape; public: ShapePtr(Shape* s) : shape(s) {} ShapePtr(const ShapePtr& other) : shape(other->clone()) {} void swap(ShapePtr& other) { std::swap(shape, other.shape); } ShapePtr& operator= (const ShapePtr& rhs) { ShapePtr tmp(rhs); swap(tmp); return *this; } // still make it behave like a pointer Shape& operator*() { return *shape; } Shape* operator->() { return shape; } };

templateclassDeepPtr{T*ptr;public:DeepPtr(T*p):ptr(p){}DeepPtr(constDeepPtr&other):ptr(other->clone()){}~DeepPtr(){deleteT;} voidswap(DeepPtr&other){std::swap(ptr,other.ptr);}DeepPtr&operator=(constDeepPtr&rhs){DeepPtrtmp(rhs);swap(tmp);return *this;}T&operator*()const{return*ptr;}T*operator->()const{returnptr;}}; -->size=2width="100%"align=center>

Smart Pointers: Reference Counting


Theabovesolutionstothecopyingproblem,destructivecopy,nocopyanddeepcopy,maintaintheinvariantthatthereisexactlyonepointer pointingtoeachobject.However,sometimeswewantmultiplepointerstosharethesameobject.Wedistinguishbetweentwosuchcases: Invisible sharing.Fromuser'spointofview,thereisnosharing.Thisisusefulasanoptimizationtoreducethecostofcopyinglarge objectssuchasstrings. Visible sharing.Usedwhenanobjectneedstobeaccessedthrougmultipleroutes,forexample,asetofobjectsstoredinmultiple associativecontainerswithdifferentsearchkeys.(Notethatreferencecountingisnotsuitableforcyclicdatastructuressuchasgraphs, becausecycleswouldnevergetdeleted.) Onesolutionistohavejustoneowningpointerperobjects.However,thisrequiresaguaranteethattheowningpointerliveslongerthanall

file://C:\Users\rmanoj\Desktop\Untitled.htm

1/13/2010

Page 36 of 72

thenon-owningones.Asaferandmoreuser-friendlyalternativeisreference counting.Thepointed-toobjecthasanassociatedcounter keepingtrackofthenumberofpointersreferringtoit.Theobjectisdeletedwhenthecounterdropstozero.

Reference counting smart pointers can be classified into intrusive (the counter is stored as a part of the object) and non-intrusive (the counter is stored separately). The non-intrusive variants can be used with any types of objects (just as ordinary pointers) but they cannot be implemented without some overhead. In the two non-intrusive variants illustrated below, the penalty is either large pointer or a more costly dereference operations. Furthermore, extra memory allocation is needed for the counter. Boost shared_ptr uses the design on the right.

The intrusive smart pointer requires modifying the pointed-to class to provide the counter. If we are in a position to design the class from scratch, it often makes sense to use handles instead of pointers. A handle does not behave like a pointer but like the object it points to. Instead of dereference operations it has all the public member functions of the pointed-to class. The pointed-to object (representation) contains the data members and the reference counter, but might not have any of the public member functions. For example, the standard string class is a handle. The common functionality of handles and representations can be factored out into common base classes. Note that the handle is a class template parameterized by the representation it refers to. The representation is supposed to contain a count variable, for example, by deriving from the class Rep:
struct Rep { int count; Rep() : count( 1) {} }; // Precondition: REP must have a member 'int count'. template < class REP > class Handle { protected: // Invariant: ptr is always != 0. REP* ptr; public: Handle() : ptr( new REP) {} Handle( const REP& rep) : ptr( new REP(rep)) {} Handle( const Handle& x) : ptr(x.ptr) { ++ptr->count; } ~Handle() { if ( --ptr->count == 0) delete ptr; } void swap(Handle& other) { std::swap(ptr, other.ptr); } Handle& operator= (const Handle& other) { Handle tmp(other); swap(tmp); return *this; } };

Thefollowingexampleshowshowtousetheseclassestoimplementaclassforintegersthatusesreferencecounting(ofcourse,reference countingdoesnotpayoffforplainintegers).
struct Integer_rep : public Rep { int i; Integer_rep() {} Integer_rep( int j) : i(j) {} }; class Integer : public Handle<Integer_rep> { public: Integer() {} Integer( int i) : Handle<Integer_rep>(i) {} int value() const { return ptr->i; } };

Inaprogram,ahandlecanbeusedjustasanormaltype.(seealsohandle_ref.Cforthefullexample,andhandle_ref_extended.Cforamore protectedexample,wherethereferencecountisaprivatevariabletoprotectthereferencecountingschemefromusercode)
int main() { Integer a(5); Integer b(a); a = b; assert( b.value() == 5); }

Sofar,therepresentationhasbeenconstant.Allowingmodificationsisaproblemwhenthesharingininvisible,becauseuserdoesnotexpect thatthemodificationofoneaffectsanother.Asolutionisastrategycalledcopy-on-write.Beforemodifyingtherepresentation,thecountis

file://C:\Users\rmanoj\Desktop\Untitled.htm

1/13/2010

Page 37 of 72

checked.Ifitisgreaterthan1,anewcopywillbecreatedinwhichthemodificationtakesplace.

Exception Safety
Whenanerroroccursinaprogram,therearetwopossibleresponses: terminate recoverandcontinue Oftenwewanttodoboth.Thatis,wewanttoterminatealowleveloperationbutrecoverandcontinueatahigherlevel.Thenweneedto transfertheexecutionfromthepointoferrortothepointofrecovery.Thetwopointsmaybeseparatedbyalongchainoffunctioncalls, whichwecalltheerror path.Somecleanupmaybenecessaryontheerrorpath:destroyingobjects,freeingdynamicmemory,closingfiles, cancellingpartialchanges,etc..Thefunctionsontheerrorpathmaycomefromdifferentsources,includinglibraries.

Exceptions are a mechanism in C++ to handle this kind of error situations. An exception is an object (of an arbitrary type) that is thrown at the point of error and caught at the point of recovery. It may carry information about the error. All automatic (non-static) local objects on the error path are destroyed by calling their destructor. Functions on the error path may also catch the exception, perform cleanup, and re-throw the (same or a different) exception.
void error() { throw "error"; } void unsafe() { int* a = new int[100]; error(); delete[] a; } // point of error

// leak! // never executed

void safe_the_hard_way() { int* a = new int[100]; // safe now try { unsafe(); } catch (...) { // catch anything delete[] a; throw; // re-throw the original exception } delete[] a; } void safe_the_easy_way() { vector<int> v(100); auto_ptr<int> p(new int(42)); safe_the_hard_way(); }

// safe: destructor cleans up // safe: destructor cleans up

int main() { try { safe_the_easy_way(); } catch (std::exception& e) { // does not catch "error" cout << e.what() << endl; // but would catch std::bad_alloc } catch (const char* s) { // point of recovery cout << s << endl; } }

Afunctionisexception-safeifitperformspropercleanupforanypossibleexception,andexception-neutralifitpropagatesallexceptionstothe caller.Therearethreelevelsofexceptionsafety: 1. Basic guarantee: No resource leaks.Theguaranteeincludesindirectresourceleaks.Forexample,amemberfunctionmaynotleavean objectinastate,wherethedestructormightnotbeablethefreeallresources. 2. Strong guarantee: Commit-or-rollback.Thefunctioneithercompletestheoperationitwasperformingorleavestheprogramstate unchangedasifitwasnevercalled. 3. Nothrow guarantee: Never emits an exception. Itisdifficultorimpossibletomakeafunctionexception-safeand-neutralifitcallsafunctionthatisnotexception-safeand-neutral.Oneweak linkcandestroytheguarantee.Forthisreasonlibrarycodeshouldbeexception-safeand-neutral.Templatesareparticularlyvulnerablesince theyknowlittleabouttheexceptionsthetemplateparametersmightthrow.

Guideline: Resource Acquisition Is Initialization


Thepreferredwaytoachieveexceptionsafetyistodoresourceacquisition(suchasmemoryallocation)inaconstructorandletthedestructor takecareofcleanup.Thistechniqueisfrequentlyreferredtoas``resourceacquisitionisinitialization''.Asdemonstratedbythe safe_the_easy_way()functionabove,standardlibraryprovidesconvenientfacilitiessuchascontainersandauto_ptrforthis.Ifthe standardlibraryfacilitiesarenotenough,itoftenmakessensetowriteaseparateclassjustformanagingaresource.Note:``aresource''(see nextguideline).

file://C:\Users\rmanoj\Desktop\Untitled.htm

1/13/2010

Page 38 of 72

Writing exception-safe constructors and destructors requires some extra care. A destructor should never emit an exception. Recall that a destructor may be called during exception handling. A desctructor throwing in this situation terminates the program immediately. Furthermore, the destructor of a container (even a basic array) calls the destructor of all its elements. If one of the element destructors throws, the resulting behavior is undefined. Therefore, if a destructor has to do something that might throw, it should catch the exception:
X::~X() { try { write_log("Destroying X"); } catch (...) { } // catch everything without re-throwing }

If a constructor throws, the object remains partially constructed, which means that the destructor is never called.
class Person { Image* img; void init(); public: Person(const Image& i) : img(new Image(i)) { init(); } ~Person() { delete image; } };

// leaks if init() throws

Fullyconstructedsubobjects(membersandbases)aredestroyed.Thusthesimplecurehereistouseauto_ptr:
class Person { auto_ptr<Image> img; void init(); public: Person(const Image& i) : img(new Image(i)) { init(); } // safe now ~Person() { } // bonus: empty destructor };

Guideline: Separate Responsibilities


Afunctionoraclasswithmultipleresponsibilitiesishardertomakeexception-safethanonewithasingleresponsibility.Movingeach responsibilitytoadifferententityhelps.Forexample,inthePersonclassabove,movingtheresponsibilityfortheimagememoryto auto_ptrwasthekey.

Here is a function with two responsibilities: incrementing a counter and returning a value. Is it exception-safe?
int counter = 0; string f(const Person& x) { ++counter; return x.name(); }

Atthebasiclevel,yes.Atthestronglevel,no.Ifx.name()throws,thefunctiondoesnotcompleteitsjob(whichincludesreturningastring), butthecounterhasbeenincremented.Whataboutthis?
int counter = 0; string f(const Person& x) { string tmp = x.name(); ++counter; return tmp; }

Stillnotstronglyexception-safe.Returninginvolvesastringcopyconstruction,whichmightthrow.Wecanachievestrongguaranteeby returningapointertostring(copyingapointercannotthrow).Abetteralternativeistouseauto_ptr:
int counter = 0; auto_ptr<string> f(const Person& x) { auto_ptr<string> tmp(new string(x.name()); ++counter; return tmp; }

Hereweachievedexception-safetybychangingthereturnvalueofthefunction.Thisisoftenundesirable.Forexample,astackoperationpop ()thatbothremovesthetopelementandreturnsit,hasasimilartwo-responsibilitiesproblem,butchangingthereturntypetoauto_ptris notreallyacceptable.Forthisreason,theSTLstack(adapter)splitsthetworesponsibilitiesbetweentwofunction:top()returnsthetop elementandpop()removesit.(See[Sutter99,pp.25-54].)

Another point illustrated by these examples is that exception safety is not just a matter of implementation details: it can affect the interface. Thus exception safety should be considered early in the design process.

Guideline: Separate Throwing Code form Critical Code


Considerthefollowingline.
void f(auto_ptr<int>(new int(42)), g());

Theorderofevaluationoffunctionargumentsisnotspecifiedbythestandard.Intheaboveline,theevaluationordercouldbenew int(42), g(),auto_ptr<int>(...).Thiscouldleadtomemoryleakifg()throws.Thefollowingisbetter.

file://C:\Users\rmanoj\Desktop\Untitled.htm

1/13/2010

Page 39 of 72

auto_ptr<int> (new int(42)); void f(p, g());

More generally, it is useful to separate the code that might throw an exception from the code that performs critical operations, preferably doing the throwing code first. The throwing code could be performed on automatic local objects that get destroyed if an exception is thrown, effectively canceling the operation. A good example is the canonical copy assignment operator implemented in terms of a copy constructor and a swap:
struct A { // ... A(const A& other); // copy constructor void swap (const A& other) throw(); // swap *this with other A& operator= (const A& other) { // copy assignment A tmp(other); // if this throws, operation has no effect swap(tmp); // can not throw return *this; } // ... }; Theonlyplaceinoperator=,whereanexceptioncouldbethrown,isthecopyconstruction.Ifthishappens,theoperationexitswithoutany permanenteffect.Thus,wehavethestrongguarantee.Theguaranteereliesontheno-throwguaranteeofswap.Usuallyswapperformsa

bitwiseswap(forexample,swappingpointersnotpointees)andcanbeimplementedusingonlycopyoperationsonbuilt-intypes,which cannotthrow.

The no-throw guarantee of swap was formalized in the exception specification throw(). In the general form, an exception specification lists the exception that might be thrown. Exception specifications should be used carefully. While they look similar to const specifications, they are less useful:
Violationsarediscoveredatrun-time,notatcompile-timeaswithconstspecification,andtheycauseanimmediateterminationofthe program. Determiningwhatexceptionscouldpossiblybethrownisnoteasy.Inparticular,templatescannotbeexpectedtoknowwhat exceptionstheirtemplateparametertypesmightthrow. Additionofanewexceptioncouldcausealotofchangesifexceptionspecificationsareusedfrequently. Therecouldbearun-timeoverheadevenifnoexceptionisthrown. Notethatalackofformalexceptionspecificationinthecodeisnotanexcuseforomitting(amoreinformal)exceptionspecificationinthe documentation.

Associating Data to Items in a Data Structure


Givenadatastructurewithitems,forexample,agraphwithnodesandedges,wewanttoassociateanadditionaldatafieldwitheachitem.A typicalapplicationwouldbeabooleanfieldforagraphtraversalalgorithm,depthfirstsearch.Thisapplicationalsohighlightsapossible dynamicnatureofthisassociatedfields,onlyaparticularsub-algorithmmightrequirethem.Severalsolutionsarepossible: Provide all possible fields:Makethefieldarequirementforthedatastructureandimplementallfieldsthatcomeupinthelibrary. Provide some general purpose fields:Forexample,theStanfordGraphBase,aClibraryforgraphalgorithmsinCbyD.E.Knuth,hassix utilityfieldsandmacrostoaccessthesefieldsasintegervalues,typedpointerstoedges,verticesorotheruses.Thealgorithmshaveto cooperateontheuseofthesefields. Manage some general purpose fields dynamically:Thissolutionisspecificallyusefulforbooleanfields.Provideabitfield,e.g.,an integer,ineachitem.Letthedatastructuremanagewhousesthedifferentbitsinthebitfielddynamically.Analgorithmcanallocate fromthedatastructureabitmaskanditcanusethisbitmasktomanipulatethecorrespondingbitinthebitfieldoftheitems.Afterthe algorithmfinishesitreturnsthebitmasktothedatastructureandthebitisfreetobeusedbysomeotheralgorithm.However,ithasto beconsideredwhatshouldhappenifthedatastructurerunsoutofbitmasks.Algorithmscouldstatethenumberofneededbitsas precondition,ortheycouldimplementanalternativemethodtocreateadditionalbits,seebelow. Let each node manage additional fields dynamically:Eachnodecontainsadictionary(hasharray)thatcontainsname-valuepairs. Additionalattributescanbejustaddedtothedictionary. Templates:Insteadofhardcodingallfields,makethematemplateparameter.Theuserhastogivetherightarguments(atcompile time)dependentonthealgorithmstheuserwantstouse.Thebenefitisthesavedspaceifsomealgorithmsarenotused. Derivation:Deriveamorespecializeditemtypeandprogramthedatastructureandthealgorithmintheobject-orientedstylesuchthat theycancopewithderivedclasses.Thissolutioncanintroduceanoticableruntimeoverheadifrathersmallfunctionshavetobemade virtual.Toaccessthespecializeditemtypeadynamiccastisusuallyneeded. Enumerate nodes and associate fields dynamically at runtime with an array:Anarrayisusedtocreatewithinanalgorithmadditional fieldsthatareassociatedwiththeenumerateditemsofthecontainerclass.Thisisalimitedsolutionincasetheenumerationiseasyand thedatastructuredoesnotchange.Itcanhidetheneedforspecificfieldsfromtheuser. Associate fields dynamically at runtime with a hash array:Thisisthemoreflexiblevariantofthepreviousitem.Insteadofasimple arrayahasharrayoranassociativemapisusedtocreatetheadditionalfields.Thisisthemostflexibleandeasy-to-usesolutionandit canhidetheneedforspecificfieldsfromtheuser.However,hashingisquitecostlyifitisonlyneededtoassociateabooleanwitheach node. Adaptor pattern:Withtheadaptorpattern,anewdatastructure,theadaptor(a.k.a.wrapper),iswrittenusingtheolddatastructure underneath.Changesinthenewdatastructurearealsoreflectedintheunderlyingolddatastructure.Newdatacanbeintegratedinthe adaptor.Thissolutionisasflexibleandeveneasiertousethanthehashmapsinthepreviousitem.However,implementinganadaptor justtoaddabooleanforadepthfirstsearchisabitofanoverkillandcanbequitesomeefforttosupportalloperationsonagraph

file://C:\Users\rmanoj\Desktop\Untitled.htm

1/13/2010

Page 40 of 72

class.Theadditionalpointersforlinkingtheadaptorwiththeadapteecostsalsosomeadditionalspace. LEDAgraphsofferseveralofthesepossibilities:generalpurposefields,templates,arrays,andhasharrays.Agenericprogrammingapproachis takenbytheBoostGraphLibrary[Siek01],wherealgorithmsareimplementedusingproperty mapsthathidetheactualmechnism.For example:


template <class Vertex, class NameMap> void foo(Vertex v, NameMap name) { typedef typename boost::property_traits<NameMap>::value_type value_type; value_type oldname = get(name, v); // get old name value_type newname = "New"; put(name, v, newname); // assign new name value_type& name_of_v = name[v]; assert(name_of_v == newname); // check the change name_of_v = oldname; // restore old name } Theexampleillustratesthethreeoperationsonpropertymaps:get(),put(),andoperator[]().Eventhesearenotprovidedforall

propertymaps.Therearefourpropertymapcategories(similartoiteratorcategories): Concept Refinement of Syntactic requirements


get() put() ReadablePropertyMap CopyConstructible WritablePropertyMap CopyConstructible

ReadWritePropertyMap ReadablePropertyMap,WritablePropertyMap - LvaluePropertyMap ReadWritePropertyMap operator[]()

For example, a boolean implemented as a bit in an integer cannot be a model of LvaluePropertyMap, because it is not possible to have a reference to it.

Solving Mutual Dependencies with the Barton-Nackman Trick


ThefollowingC++techniqueisusuallyreferredtoastheBarton-Nackmantricksincetheyhaveintroduceditintheirbook[page352, Barton97].However,[Coplien95]documentssomemoreoccurrencesofthese"curiouslyrecurringtemplatepatterns".

We start with a simple class that, among other operations, provides an equality and an inequality comparison operator.
class A { public: bool operator == (const A& a) const; bool operator != (const A& a) const { return ! (*this == a); } // ... };

Theinequalitycomparisonoperatorisimplementedintermsoftheequalityoperator.Wouldn'titbenicetofactoroutthisgeneric implementationintoabaseclassandshareitwithallclassesofthiskind?Theproblemisacyclictypedependency.Ofcourse,the(then) derivedclassAneedstoknowthebaseclass.Butthebaseclassneedstoknowthederivedclassaswell,sinceotherwiseitcannotcallthe correctequalityoperator,andthederivedclassshowsupintheoperatorstypesignatureaswell.Here,thesolutionistoinjectthederived classasatemplateargumentintothebaseclass.


class A : public Inequality<A> { public: bool operator == (const A& a) const; };

Sinceweintendtocalltheequalityoperatorofthederivedclass,wehavetouseatypecast,whichisasafedown-castinthiscase.(seealso barton_nackman.C)
template <class T> class Inequality { public: bool operator != (const T& t) const { return ! (static_cast<const T&>(*this) == t); } };

Thesametechniquecanbeusedtoimplementabaseclassforiteratorsthatcontainsallthosesmallmemberfunctionsthataredefinedin termsofamuchsmallersetofmemberfunctions.Evenbetter,sincethebaseclassisatemplateclass,wecanmakeuseofthe"lazy"implicit instantiationandimplementthemostgeneralbaseclassforiteratorsoftherandomaccesscategory.Ifwederiveaniteratoroftheforward category,theextrarandomaccessoperatorsinthebaseclassarejustignoredanddon'tcauseerrormessagesaslongastheyarenotused. (seealsoIterator_base.handIterator_base.C)

There is only one pitfall with this solution: name lookup rules for overloaded functions in class hierarchies. The name lookup stops as soon as the function name has been found, and it does not search for more overloaded function in base classes. The pre- and postincrement operator are an example (note also the use of a private member function to encapsulate the type cast, which is also constoverloaded):
template < class Derived> class Iterator_base {

file://C:\Users\rmanoj\Desktop\Untitled.htm

1/13/2010

Page 41 of 72

Derived& derived() { return static_cast<Derived&>(*this); } const Derived& derived() const { return static_cast<const Derived&>(*this); } public: const Derived operator++(int) { Derived tmp = derived(); ++ derived(); return tmp; } // ... }; class Some_iterator : public Iterator_base< Some_iterator> { // ... public: Self& operator++(); // ... }; int main() { Some_iterator i; i++; }

Thepost-incrementcallcausesthecompilertolookforthefunctionnameoperator++(withouttypesignatureforthearguments)intheclass hierarchy.Itfindsthepre-incrementoperatorinSome_iteratorandstopswiththefunctionlookup.Onlyoverloadedinstancesofthe functioninthisclassandglobalfunctionsarenowusedtoresolvethefunctioncall.Thecompilerdoesnotfindthecorrectpost-increment operatorinthebaseclassandgivesanerrormessage.Thesolutionisaworkaround;implementalloverloadedfunctionsinthebaseclassand givetheinvolvedfunctionsinthederivedclassanewname,seeIterator_base.h.

Solving Mutual Dependencies between Class Templates


Agraphconsistsofnodesandedges.Anodeknowsincomingandoutgoingedges,anedgeknowsitstwoincidentnodes.Implementingthis cyclictypedependenciesbetweennodeandedgetypewoulduseaforwarddeclarationinC:
struct Edge; struct Node { Edge * edge; // .... maybe more than one edge .... }; struct Edge { Node * source; Node * dest; };

Ifwewanttoparameterizenodesandedgeswithatemplateparameterforsomeadditionalauxiliarydata,wecanfollowthesameidea.But sincethenodehastoknowthetypeoftheedge,andtheedgehastoknowthetypeofthenode,bothalsohavetoknowthetemplate parameterofeachother.Asolutioncouldlooklikethis(seealsograph.Cforanexamplewithanadditionalgraphclass).


// forward declaration template <class A, class B> struct Edge; template <class A, class B> struct Node { Edge<A,B> * edge; A aux; }; template <class A, class B> struct Edge { Node<A,B> * node; B aux; }; int main() { Node<int,double> node; Edge<int,double> edge; node.edge = &edge; edge.node = &node; node.aux = 5; edge.aux = 6.6; }

// node with

Onedisadvantageofthissolutionisthattheauxiliarydataisalwaysthereandconsumesspace,evenifitisnotneeded.Itwouldbeniceto specifyitasvoidinthiscase.However,alocalvariablevoid auxisnotgoingtoworkinC++.However,wecanusepartialspecializationto writeaspecializesversion,hereforthenode,togetridofthereservedauxiliarydataifwespecifyittobevoid.


template <class B> struct Node<void,B> { Edge<void,B> * edge; };

Inthissolutionthenodetypeandtheedgetypearetightlycoupledtogether(asintheCsolution).Inthespiritofgenericprogrammingwe

file://C:\Users\rmanoj\Desktop\Untitled.htm

1/13/2010

Page 42 of 72

mightwanttodecouplethem.ImaginetoexchangeavertextypeNodewithanothervertextypeNode'asinthefollowingfigure.

We might specify a concept for a node and a concept for an edge. Now, given a model for a node and a model for an edge they should work together in a graph class. We use a similar mind-twister as for the Barton-Nackman trick in the previous section. The missing type information for a node as well as for the edge is provided with a template parameter Graph. There is a graph class that takes two parameters, a class template for a node and a class template for an edge (note the nested template declarators to pass a class template as a template argument). The graph class uses itself as parameter to the node template and the class template to define the local types for node and edge. To summarize, the graph knows the node and the edge class that are supposed to work together, and therefore the graph class passes itself as template argument to both types.
template <class Graph> struct Node { typedef typename Graph::Edge Edge; Edge* edge; // .... maybe some more edges .... }; template <class Graph> struct Edge { typedef typename Graph::Node Node; Node* node; }; template < template <class G> class T_Node, template <class G> class T_Edge> struct Graph { typedef Graph< T_Node, T_Edge> Self; typedef T_Node<Self> Node; typedef T_Edge<Self> Edge; }; int main() { typedef Graph< Node, Edge> G; G::Node node; G::Edge edge; node.edge = &edge; edge.node = &node; }

Toillustratetheflexibilityinthisdesign,weimplementanewnodeclassbyderivingfromtheoldoneandaddamembervariableforcolor(see alsograph2.Cforthisexample,andgraph3.Cforamoreextensiveexample).
template <class Graph> struct Colored_node : public Node<Graph> { int color; }; int main() { typedef Graph< Colored_node, Edge> G; G::Node node; G::Edge edge; node.edge = &edge; edge.node = &node; node.color = 3; }

Itisimportanttounderstandthatthesecyclicdefinitionswork--asfortheCexample--becausewecanmakeuseofadeclaredtypetodefine pointersandreferencestothistypebeforethistypeisdefineditself.Forexample,wecannotchangethepointermemberEdge * edgeof thenodeclasstoavalueEdge edge.

Const correctness is an issue for a graph data structure. For example, declaring a node constant, it should not be possible to traverse the graph in any fashion and to reach a mutable node or edge. For the more extensive example graph3.C the adjacency list Edge_list edges is of particular interest. This list contains iterators to edges, but declaring the node constant, all access to the contents of this list has to return const_iterator's to edges.

Double Dispatch, Making a Function Virtual for Two Arguments


Imagineagameinspacewithships,basestationsandasteroids(seealso[Item31,Meyers96]).Collisionsarehandledasfollows:

file://C:\Users\rmanoj\Desktop\Untitled.htm

1/13/2010

Page 43 of 72

Ship Ship damageproportionaltospeed

Station dockingifslow,elsedamage damageproportionaltospeed

Asteroid destroysasteroidifitsmall,elseshipsget destroyed destroysasteroidifitsmall,elsestationget destroyed

Station dockingifslow,elsedamage Asteroid

destroysasteroidifitsmall,elseshipsget destroysasteroidifitsmall,elsestationget asteroidsbreakintosmallerpieces destroyed destroyed

Assume all three objects are derived from a single abstract base class. We start with defining a virtual collision handling function that takes the second game object as a parameter.
struct Game_object { virtual void collision( Game_object* other) = 0; virtual ~Game_object() {} }; struct Ship : public Game_object { virtual void collision( Game_object* other) { // this (of type Ship) collides here with other } }; struct Station : public Game_object; // similar struct Asteroid : public Game_object; Eachcollisionknowsthecorrecttypeofitsthispointer,whichresolvesthefirstdispatchalongthetypeofthefirstargument.Buttheother

pointerisstilloftheabstractbaseclasstype,theseconddispatchalongthetypeofthesecondargumentremainsunresolvedyet.Wecould useaswitch/casestatementtodistinguishitsactualtype(usingruntimetypeinformation(RTTI)withthetypeidfunction).However,the object-orientedwaydoesn'tlikethis.Switch/casestatementstendtobeunmaintainableandarenotextendible.Apossibleobject-oriented solutionusesasetofvirtualmemberfunctionstodipatchalongthesecondargument(seedouble_dispatch_static.Cforthisexample).


struct Game_object { virtual void collision( Game_object* other) = 0; virtual void collision2( Ship* other) = 0; virtual void collision2( Station* other) = 0; virtual void collision2( Asteroid* other) = 0; virtual ~Game_object() {} }; struct Ship : public Game_object { virtual void collision( Game_object* other) { // this (of type Ship) collides here with other, call second dispatch other->collision2( this); } virtual void collision2( Ship* other) { // Ship collides with ship. }; virtual void collision2( Station* other) { // Ship collides with Station. }; virtual void collision2( Asteroid* other) { // Ship collides with Asteroid. }; }; struct Station : public Game_object {}; // similar struct Asteroid : public Game_object {};

Iftheclasshierarchyisknownanddoesnotchangeinthefuture,thisisthesolution.But,thissolutiondoesnotsolvetheproblemof extendibility.Addinganothergameobject,forexamplestars,wouldrequiretoaddavirtualmemberfunctiontoeachclasshandlingstarsnow.

Furthermore, we can observe that the collision handling is symmetric with respect to its two arguments. But we probably do not want to implement each function twice. One extendible solution would be to implement double dispatch by hand as a (dynamic) array of function pointers that encode the collision handling table from above. We would implement the two-dimensional extension of the one-dimensional virtual function pointer table that used by the compiler to implement virtual member functions. Since we don't have the support of the compiler here (convinient index generation into this table etc.), the solution described at the end of [Item 31, Meyers96] is rather complicated and uses STL maps. A simpler solution uses an if/then/else cascade to decide for each class using RTTI with which other classes it can handle the collisions. For unknown types, it calls the collision function again, but with the reversed order of arguments. Among the classes has to exist an order. A class has to implement the collision for all classes that are smaller in this order (in other words, a new class has to implement the collision with all old classes). There is the danger of missing to test for one class, in which case this solution produces an infinite loop (until the stack oveflows). This can be solved by breaking the symmetry, and give the second call with reversed parameters a different name (see double_dispatch_ext.C for this example). We change the problem slightly and think of two different types that should interact with each other. An example would be a tree hierarchy composed of different node types, and a set of algorithm that work on the tree nodes. We can make this double dispatch problem extendible for either one of these types. We can just encode the algorithms as member functions in the different tree node types. Now it is easy to add a new tree node type. Or we can use the visotor pattern, see the Section Visitor Pattern, to make the family of algorithms extendible.

file://C:\Users\rmanoj\Desktop\Untitled.htm

1/13/2010

Page 44 of 72

Templates could be used to solve the double dispatch problem conveniently if we change the setting slightly. Since templates work at compile time we have to know the actual types of colliding objects and we cannot work with base class pointers. The generic template implements the symmetry, the specialized functions implement the actual collision handling. This solution is extendible. A new class has to implement all possible combinations with old classes.
template < class T1, class T2> void collision( T1& o1, T2& o2) { collision( o2, o1); } void collision( Ship& o1, Ship& void collision( Ship& o1, Station& void collision( Ship& o1, Asteroid& void collision( Station& o1, Station& void collision( Station& o1, Asteroid& void collision( Asteroid& o1, Asteroid&

o2); o2); o2); o2); o2); o2);

6. CGAL, the Computational Geometry Algorithms Library


Introduction
Computationalgeometryisthesub-areaofalgorithmdesignthatdealswiththedesignandanalysisofalgorithmsforgeometricproblems involvingobjectslikepoints,lines,polygons,andpolyhedra.Overthepasttwodecades,thefieldhasdevelopedarichbodyofsolutionstoa hugevarietyofgeometricproblemsincludingintersectionproblems,visibilityproblems,andproximityproblems.Anumberoffundamental techniqueshavebeendesigned,andkeyproblemsandproblemclasseshaveemerged.

Geometric algorithms arise in various areas of computer science. Computer graphics and virtual reality, computer aided design and manufacturing, solid modeling, robotics, geographical information systems, computer vision, shape reconstruction, molecular modeling, and circuit design are well-known examples. To a large extent the theory has been developed with asymptotic worst-case complexity analysis and under the assumption of the real RAM model. Computations with real numbers are assumed to be in constant time. For many (perhaps most) algorithms and problems this is a justified assumption that can be perfectly simulated with finite precision numbers if the input has limited precision. In 1996 the Computational Geometry Impact Task Force published a task force report, Application Challenges to Computational Geometry. Although crediting the remarkable success of the field in theory, the report now demanded to address also the applicability in practice. Recommendation number one on the list of four was ``production and distribution of usable (and useful) geometric codes''. Where are the difficulties in doing so? There are four major reasons why implementing geometric algorithms in particular is seen to be more difficult than in other fields:
Algorithmsingeometryareamongthemostadvancedalgorithmsinalgorithmdesignandtheymakefrequentuseofcomplicateddata structures.

Furthermore, software engineering has ignored the difficulties in algorithm engineering for quite a while. For example, the main focus in object-oriented design is on data abstraction, data encapsulation, relationships among data, its reuse and the design of large-scale systems. Only recently have implementations of algorithms been rediscovered as an active topic in software engineering, for example with the generic programming paradigm [Musser89].
Theasymptoticworst-casecomplexityanalysisdoesnotmatchthepracticalneedfortworeasons.Inpractice,theconstanthiddenin theasymptoticanalysiscaneasilyoutweighasymptoticfactors,suchaslog-factors,andtheworst-caseusuallydependsonageneral inputmodelthatmaybeunrealistic.Morerealisticinputmodels,suchasfatness,canhelpunderstandingthepracticabilityof algorithms. Theoreticalpapersassumeexactarithmeticwithrealnumbers.Thecorrectnessproofsofthealgorithmsrelyonexactcomputation,and replacingexactarithmeticbyimprecisebuilt-infloating-pointarithmeticdoesnotworkingeneral.Geometricalgorithmsinparticular aresensitivetoroundingerrorssincenumericaldataandcontrolflowdecisionshaveusuallyastronginterrelation.Thenumerical problemsmaydestroythegeometricconsistencythatanalgorithmmayrelyon.Asaresulttheprogrammaycrash,mayrunintoan infiniteloop,or--perhapsworstofall--mayproduceunpredictableerroneousoutput.

The requirements on the arithmetic vary with the algorithm. Some algorithms require only sign computations of polynomial expressions of bounded degree in the input variables. Others require unbounded degree or algebraic roots. Various packages for exact arithmetic are available for different needs. Another approach is to redesign the algorithm to cope with inexact arithmetic. Usually the output is only an approximation of the exact solution. As a common prerequisite for exact arithmetic the input is rounded, either to convert the input into the format required for the arithmetic (rounding floating point to integer), or to lessen the precision requirements on the arithmetic.
Often,theoreticalpapersexcludedegenerateconfigurationsintheinput.Typically,thesedegeneraciesarespecifictothealgorithmand theproblem,andwouldinvolvethetreatmentofspecialcasesinthealgorithm.Simpleexamplesofconfigurationsconsideredas degenerateareduplicatepointsinapointsetorthreelinesintersectinginonepoint.Forsomeproblems,itisnotdifficulttohandlethe degeneracies,butforotherproblemsthespecialcasetreatmentdistractsfromthesolutionofthegeneralproblemanditcanamountto aconsiderablefractionofthecodingeffort.

In theory, this approach of excluding degeneracies from consideration is justified by the argument that degenerate cases are very rare in the set of all possible input over the real numbers, i.e., they have zero probability if the input set is randomly

file://C:\Users\rmanoj\Desktop\Untitled.htm

1/13/2010

Page 45 of 72

chosen over the real numbers. Another argument is that it is first of all important to understand the general case before treating special cases. In practice, however, degenerate input occurs frequently. For instance, the coordinates of the geometric objects may not be randomly chosen over the real numbers, but lie on a grid. They may be created by clicking in a window in a graphical user interface. In some applications, what are called degeneracies are even high-valued design criteria. In architecture features of buildings do align on purpose. As a consequence, practical implementations usually must address the handling of degeneracies. General approaches in handling degeneracies are symbolic perturbation or randomized perturbation with performance and correctness guarantee.
Thecommunityhasaddressedthesetopicsfromtimetotimeandwithincreasingintensity(severalreferencesaregivenabove),butmany usefulgeometricalgorithmshavenotfoundtheirwayintotheapplicationdomainsofcomputationalgeometryyet.Thissituationisalsoa severehindranceforresearchersiftheywishtoimplementandevaluatetheiralgorithms.Thus,theconstantshiddenintheanalysisofthe otherwisetheoreticallyefficientalgorithmsoftenisnotknown.

To remedy this situation the Computational Geometry Algorithms Library, CGAL (http://www.cgal.org/), has been started five years ago in Europe in order to provide correct, efficient, and reusable implementations [Fabri99]. The library is being developed by several universities and research institutes in Europe and Israel. The major design goals for CGAL include correctness, robustness, flexibility, efficiency, and ease-of-use. One aspect of flexibility is that CGAL algorithms can be easily adapted to work on data types in applications that already exist. The design goals, especially flexibility and efficient robust computation, led us to opt for the generic programming paradigm using templates in C++, and to reject the object-oriented paradigm in C++ (as well as in Java). In several appropriate places, however, we make use of objectoriented solutions and design patterns. Generic programming with templates in C++ also provides us with the help of strong type checking at compile time. Moreover, the C++ abstractions used by us do not cause any runtime overhead. The birth of the CGAL-library dates back to a meeting in Utrecht in January 1995. Shortly afterwards, the five authors of [Fabri99] started developing the kernel. The CGAL-project has been funded officially since October 1996 and the team of developers has grown considerably among professionals in the field of computational geometry and related areas, in academia especially research assistants, PhD students and postdocs. The CGAL release 1.2 of January 1999 consists of approximately 110,000 lines of C++ source code for the library, plus 50,000 lines for accompanying sources, such as the test suite and example programs, not counting C++ comments or empty lines (the release 2.4 of May 2002 consists of approximately 290,000 lines of code, comments and empty lines included this time, plus test suite and example programs). In terms of the elder Constructive Cost Model (COCOMO) the line counts, the people involved, and the time schedule indicate a large project comparable to operating systems or database management systems. The WWW home-page of CGAL (http://www.cgal.org/) provides a list of publications about CGAL and related research.

Related Work
Threeapproachesofdisseminatinggeometricsoftwarecanbedistinguished:collectionsofisolatedimplementations,integratedapplications orworkbenches,andsoftwarelibraries.AnoverviewonthestateoftheartofcomputationalgeometrysoftwarebeforeCGALincludingmany referencesisgivenin[Amenta97].

Collecting isolated implementations, also called the Gems approach according to the successful Graphics Gems series, usually requires some adaption effort to make things work together. Compared to the graphics gems, computational geometry implementations usually use more involved data structures and more advanced algorithms. This makes adaption harder. A good collection provides the Directory of Computational Geometry Software (http://www.geom.umn.edu/software/cglist/). Integrated applications and workbenches provide a homogeneous environment, for example with animation and interaction capabilities, and all parts work smoothly together. However, they tend to be monolithic, hard to extend, and hard to reuse in other projects. Examples date back to the end of the Eighties, specifically XYZ GeoBench (http://wwwjn.inf.ethz.ch/geobench/XYZGeoBench.html) developed at ETH Zurich, Switzerland, is one of the precursors of CGAL. Software libraries promise that the components work seamlessly together, that the library is extensible and that the components can be reused in other projects. Examples are the precursors of CGAL developed by members of the CGAL consortium. These precursors are PlaGeo developed at Utrecht University, C++Gal developed at Inria Sophia-Antipolis, and the geometric part of LEDA (http://www.mpi-sb.mpg.de/LEDA/), a library for combinatorial and geometric computing, which has been developed at Max-Planck-Institut fr Informatik, Saarbrcken. Another example is GeomLib, a computational geometry library implemented in Java at the Center for Geometric Computing, located at Brown University, Duke University, and Johns Hopkins University in the United States. They state their goal as an effective technology transfer from Computational Geometry to relevant applied fields.

Overview of the Library Structure

file://C:\Users\rmanoj\Desktop\Untitled.htm

1/13/2010

Page 46 of 72

CGALisstructuredintothreelayersandasupportlibrary,whichstandsapart.Thethreelayersarethecorelibrarywithbasicnon-geometric functionality,thegeometrickernelwithbasicgeometricobjectsandoperations,andthebasiclibrarywithalgorithmsanddatastructures.

The library layers and the support library are further subdivided into smaller modular units. The modular approach has several benefits: The library is easier to learn, the implementation work is more easily spread among the project partners, and the reduction of dependencies facilitates testing and maintenance. The geometric kernel contains simple geometric objects of constant size such as points, lines, segments, triangles, tetrahedra, circle and more. It provides geometric predicates on those objects, operations to compute intersections of and distances between objects, and affine transformations. The kernel objects are closed under affine transformations, e.g., the existence of circles implies that there are also ellipses in the kernel. The geometric kernel is split in three parts, one for two-dimensional objects, one for three-dimensional objects, and one for generaldimensional objects. Geometry in two and three dimensions is well studied and has lots of applications which explains their special status. For all dimensions there are Cartesian and homogeneous representations available for the coordinates. To solve robustness problems, CGAL advocates the use of exact arithmetic instead of floating point arithmetic. An arithmetic is associated with a number type in CGAL and the classes in the geometric kernel are parameterized by number types. CGAL provides own number types and supports number types from other sources, e.g., from LEDA or the Gnu Multiple Precision library. Since the arithmetic operations needed in CGAL are quite basic, every library supplying number types can be adapted easily to work with CGAL. The basic library contains more complex geometric objects and data structures: polygons, triangulations, planar maps, polyhedra and so on. It also contains algorithms, such as for computing the convex hull of a set of points, the union of two polygons, smallest enclosing ellipse and so on. The figure above indicates the major parts in the basic library. These parts are mostly independent from each other and even independent from the kernel. This independence has been achieved with geometric traits classes, to be discussed later. Default implementations of the traits classes use the CGAL kernel for the types and primitive operations. Other implementations of the traits classes provided in CGAL use the LEDA geometric part. The traits class requirements are simple enough for a user to be able to write a traits class for own geometric data types and operations. The core library offers basic non-geometric functionality that is needed in the geometric kernel or the basic library, for example support for coping with different C++ compilers which all have their own limitations. The core library contains the support for assertions, preconditions and postconditions. Circulators and random number generators belong here as well. The support library also contains functionality with non-geometric aspects. In contrast to the core library, this functionality is not needed by the geometric kernel nor the basic library. The support library interfaces the geometric objects with external representations, like visualizations or external file formats. Among the list of supported formats are VRML and PostScript as well as the GeomView program and LEDA windows for 2D and 3D visualization. The support library also contains generators for synthetic test data sets, for example random points uniformly distributed in a certain domain. The adaptation of number types from other libraries is contained in the support library as well. The separation from the kernel and the basic library makes the functionality in the support library orthogonal and therefore open for future extensions.

Geometric Kernel
Thegeometrickernelcontainstypesforobjectsofconstantsize,suchaspoint,vector,direction,line,ray,segment,triangle,iso-oriented rectangleandtetrahedron.Eachtypeprovidesasetofmemberfunctions,forexampleaccesstothedefiningobjects,theboundingboxofthe objectifexisting,andaffinetransformation.Globalfunctionsareavailableforthedetectionandcomputationofintersectionsaswellasfor distancecomputations.

The current geometric kernel provides two families of geometric objects: One family is based on the representation of points using Cartesian coordinates. The other family is based on the representation of points using homogeneous coordinates. The homogeneous representation extends the representation with Cartesian coordinates by an additional coordinate which is used as a common denominator. More formally, in d-dimensional space, a point with homogeneous coordinates (x0, x1, ..., xd-1, xd), where xd != 0, has Cartesian coordinates (x0/xd, x1/xd, ..., xd-1/xd). This avoids divisions and reduces many computations in geometric algorithms to calculations over the integers. The homogeneous representation is used for affine geometry in CGAL, and not projective geometry,

file://C:\Users\rmanoj\Desktop\Untitled.htm

1/13/2010

Page 47 of 72

where the homogeneous representation is usually known from. Both families are parameterized by the number type used to represent the Cartesian or homogeneous coordinates. The type CGAL::Cartesian<double> specifies the Cartesian representation with coordinates of type double, and the type CGAL::Homogeneous<int> specifies the homogeneous representation with coordinates of type int. These representation types are used as template argument in all geometric kernel types, like a two-dimensional point declared as
template <class R> CGAL::Point_2;

withatemplateparameterRfortherepresentationclass.Typedefscanbeusedtointroduceconvenientlyshortnamesforthetypes.Hereis anexamplegivenforthepointtypewiththehomogeneousrepresentationandcoordinatesoftypeint:
typedef CGAL::Point_2< CGAL::Homogeneous<int> > Point_2; TheclasstemplatesparameterizedwithCGAL::CartesianorCGAL::Homogeneousprovidetheuserwithacommoninterfacetothe

underlyingrepresentation.Thiscommoninterfacecanbeusedinhigher-levelimplementationsindependentlyoftheactualcoordinate representation.Thelistofrequirementsonthetemplateparameterdefinestheconceptofarepresentation classforthegeometrickernel.

CGAL provides clean mathematical concepts to the user without sacrificing efficiency. For example, CGAL strictly distinguishes points and (mathematical) vectors, i.e., it distinguishes affine geometry from the underlying linear algebra. Points and vectors are not the same with regard to illicit computations resulting from identification of points and vectors in geometric computations. In particular, points and vectors behave differently under affine transformations. We do not even provide automatic conversion between points and vectors but use the geometric concept of an origin instead. The symbolic constant CGAL::ORIGIN represents a point and can be used to compute the locus vector as the difference between a point and the origin. Function overloading is used to implement this operation internally as a simple conversion without any overhead. Note that we do not provide the geometrically invalid addition of two points, since this might lead to ambiguous expressions: Assuming three points p, q, and r and an affine transformation A, one can write in CGAL the perfectly legal expression A( p + ( q - r)). The slightly different expression A(( p + q) - r) contains the illegal addition of two points. However, if we allow this addition, we would expect the same result coordinatewise as in the previous, legal expression. But this is not necessarily intended, since the expression within the affine transformation is meant to evaluate to a vector, and not to a point as in the previous expression. Vectors and points behave differently under affine transformations. To avoid these ambiguities, the automatic conversion between points and vectors is not provided as well. (see Origin.C for an example point/vector/origin implementation) Class hierarchies are used rarely in CGAL. One example are affine transformations, which maintain distinct internal representations specialized on restricted transformations. The internal representations differ considerably in their space requirements and the efficiency of their member functions. For all but the most general representation we gain performance in terms of space and time. And for the most general representation, the performance penalty caused by the virtual functions is negligible, because the member functions are computationally expensive for this general representation. Alternatively we could have used this general representation for affine transformations only. But the use of a hierarchy is justified, since the specialized representations, namely translation, rotation and scaling, arise frequently in geometric computing. Another design decision was to make the (constant-size) geometric objects in the kernel non-modifiable (value semantics). For example, there are no member functions to set the Cartesian coordinates of a point. Points are viewed as atomic units, and no assumption is made on how these objects are represented. In particular, there is no assumption that points are represented with Cartesian coordinates. They might use polar coordinates or homogeneous coordinates instead. Then, member functions to set the Cartesian coordinates are expensive. Nevertheless, in current CGAL the types based on the Cartesian representation as well as the types based on the homogeneous representation have both member functions returning Cartesian coordinates and member functions returning homogeneous coordinates. These access functions are provided to make implementing own predicates and operations more convenient. Like other libraries we use reference counting for the kernel objects. Objects point to a shared representation. Each representation counts the number of objects pointing to it. Copying objects increments the counter of the shared representation, deleting an object decrements the counter of its representation. If the counter reaches zero by the decrement, the representation itself is deleted. The implementation of reference counting is simplified by the non-modifiability of the objects. However, the use of reference counting was not the reason for choosing non-modifiability. Using `copy on write' (a new representation is created for an object whenever its value is changed by a modifying operation), reference counting with modifiable objects is possible and only slightly more involved. Reference counting costs about 15% to 30% runtime for the types double and float, but gains 2% to 11% runtime for the type leda_real.

Basic Library
Thebasiclibrarycontainsmorecomplexgeometricobjectsanddatastructures,suchaspolygons,polyhedrons,triangulations(including Delaunaytriangulations),planarmaps,rangetrees,segmenttrees,andkd-trees.Italsocontainsgeometricalgorithms,suchasconvexhull, smallestenclosingcircle,ellipse,andsphere,booleanoperationsonpolygonsandmapoverlay.

Following the generic programming paradigm as introduced above, CGAL is made to comply with STL. The interfaces of geometric objects and data structures in the basic library make extensive use of iterators, circulators, and handles (trivial iterator), so that algorithms and data structures can be easily combined with each other and with those provided by STL and other libraries. An example of a geometric algorithmic problem is the computation of the convex hull. The algorithm takes a set of points and outputs the sequence of extreme points on the boundary of the convex hull. The following program computes the convex hull of 100 random points uniformly distributed in the disc of radius one centered at the origin. The point generator gets as parameter a random

file://C:\Users\rmanoj\Desktop\Untitled.htm

1/13/2010

Page 48 of 72

source. This random source is initialized with the fixed seed 1 in this example. The result is drawn in a LEDA window, first all points in black, then the hull as polygon in green and finally the vertices in red. The header file hides the usual typedefs and declares all types parameterized with the representation class CGAL::Cartesian<double>.
#include "cartesian_double.h" int main () { Random rnd(1); Random_points_in_disc_2 rnd_pts( 1.0, rnd); list<Point_2> pts; copy_n( rnd_pts, 100, back_inserter( pts)); Polygon_2 ch; CGAL::convex_hull_points_2( pts.begin(), pts.end(), back_inserter(ch)); Window* window = demo_window(); Window_iterator_point_2 wout( *window); copy( pts.begin(), pts.end(), wout); *window << CGAL::GREEN << ch << CGAL::RED; copy( ch.vertices_begin(), ch.vertices_end(), wout); Point_2 p; *window >> p; delete window; return 0; }

// wait for mouse click

ThisprogramalsoillustratestheuseoftheCGALpolygonasacontainerclass.Theback_inserteradaptorofSTLisapplicableasexpected, whichillustratesthegenerictool-boxcharacterofSTLanditsconcepts.

Triangulations are another example of a container-like data structure in the basic library. Triangulations in CGAL support the incremental construction. The following program is therefore even simpler than the previous one; without using any intermediate container to store all input points, 100 random points are copied into the triangulation data structure.
#include "cartesian_double.h" int main () { Random Random_points_in_disc_2

rnd(1); rnd_pts( 1.0, rnd);

Delaunay_triangulation_2 dt; copy_n( rnd_pts, 100, back_inserter( dt)); Window* window = demo_window(); *window << dt; Point_2 p; *window >> p; // wait for mouse click delete window; return 0; }

Themajortechnologicalachievementinthedesignofthebasiclibrarywastheconceptofthegeometrictraitsclass,seethenextSection, whichallowsthereuseofthetriangulationdatastructure,forexampletotriangulateasetofthreedimensionalpointswithrespecttotheirxyprojection(usefultoreconstructterrains.Weassumeinthefollowingexamplethattherepresentationclassforthegeometrickernelisnamed REPintheheaderfile.Theprogramreadsthree-dimensionalpointsfromcin,triangulatesthem,andwritesthetriangulationtocout.Note thatmostofthesetypedefsareequaltothosehiddenpreviouslyintheheaderfile.Theonlychangeisthegeometrictraitsclassfrom CGAL::Triangulation_euclidean_traits_2totheonegivenhere.


#include "cartesian_double.h" #include <CGAL/Triangulation_euclidean_traits_xy_3.h> typedef CGAL::Triangulation_euclidean_traits_xy_3<REP> Traits; typedef CGAL::Delaunay_triangulation_2<Traits> Triangulation_xy; int main () { Triangulation_xy dt; copy( istream_iterator<Point_3>(cin), istream_iterator<Point_3>(), back_inserter( dt)); cout << dt; return 0; }

Thetriangle-baseddatastructureandthehalfedgedatastructureusedfortheplanarmapandthepolyhedralsurfaceinthebasiclibraryare basedonthedesignofcombinatorialdatastructuresdescribedin[Kettner98].Thereviseddesignofthehalfedgedatastructureforpolyhedral surfacesisdescribedin[Kettner99].ItisbasedonthedesigninSectionSolvingMutualDependenciesbetweenClassTemplates.

Geometric Traits Classes


Ageometrictraitsclassseparatesageometricalgorithmorgeometricdatastructurefromitsunderlyinggeometrickernel.

The following example of a convex hull algorithm illustrates the use of a geometric traits class. The algorithm used is Andrew's variant of Graham's scan [Andrew79]. The actual implementation presented here stems from our framework for one sided error

file://C:\Users\rmanoj\Desktop\Untitled.htm

1/13/2010

Page 49 of 72

predicates [Kettner98b]. The implementation has been modified to use the iterator based interface from above and the traits class. Andrew's variant of Graham's scan needs only a point type and a leftturn predicate from the geometric kernel given that the input points are already sorted lexicographically. Thus, the geometric traits class is quite short in this example. The leftturn predicate for three points p, q, and r in the plane is true if the points in this order perform a left turn. For points represented in Cartesian coordinates, the predicate is equivalent to the sign of the following determinant:

In the following example, the geometric traits class for the convex hull algorithm is itself a class template parameterized with the number type NT for the point coordinates. NT is the same type as used for the Cartesian point type Point_2. The evaluation of the determinant is implemented as a function object assuming exact arithmetic and the leftturn member function gives access to the function object.
template <class NT> struct Point_2 { typedef NT Number_type; NT x; NT y; }; template <class NT> struct Convex_hull_traits { typedef Point_2<NT> Point; struct Leftturn { bool operator()( const Point& p, const Point& q, const Point& r) { return (q.x-p.x) * (r.y-p.y) > (r.x-p.x) * (q.y-p.y); } }; Leftturn leftturn() const { return Leftturn(); } };

Thealgorithmisparameterizedwithiterators.Itrequiresthatthesequenceofinputpointsfromtherange[first,beyond)ofbidirectional iteratorsislexicographicallysortedandcontainsonlypairwisedisjointpointsandatleasttwopoints.Thealgorithmcomputestheconvexhull andcopiesallpointsontheboundaryoftheconvexhull(notonlythevertices)incounterclockwiseordertotheoutputiteratorresult.Itruns inlineartimeandspaceandcanproduceupto2n-2outputpointsinthedegeneratecaseofallpointsonasegmentwherenisthenumberof inputpoints.Thelocalvectorcanbeomittedifthealgorithmcanusetheoutputcontainerasastack.Thisisbeyondthecapabilitiesofthe currentlydefinediteratorcategoriesandrestrictstheapplicabilityofthealgorithm.Furthermorethischangewoulddistractherefromthe purposeofthisexample,theillustrationoftheuseofageometrictraitsclassinageometricalgorithm.


template <class BidirectionalIterator, class OutputIterator, class Traits> OutputIterator convex_hull( BidirectionalIterator first, BidirectionalIterator beyond, OutputIterator result, const Traits& traits) { typedef typename Traits::Point Point; vector<Point> hull; hull.push_back( *first); // sentinel hull.push_back( *first); // lower convex hull (left to right) BidirectionalIterator i = first; for ( ++i; i != beyond; ++i) { while ( traits.leftturn()( hull.end()[-2], *i, hull.back())) hull.pop_back(); hull.push_back( *i); } // upper convex hull (right to left) i = beyond; for ( --i; i != first; ) { --i; while ( traits.leftturn()( hull.end()[-2], *i, hull.back())) hull.pop_back(); hull.push_back( *i); } // clean up and copy hull to output iterator hull.pop_back(); hull.front() = hull.back(); hull.pop_back(); return copy( hull.begin(), hull.end(), result); }

Callingthealgorithmwiththetraitsclassisstraightforward.Thedefaultconstructorofthetraitsclassisused.Wecanuseiterator_traits todeducethepointtypefromtheiteratortypeandfromthepointtypethenumbertypewhichcanbeusedtoprovideourgeometrictraits classConvex_hull_traitsasdefaultargumenttotheconvexhullalgorithm.Weuseanoverloadeddefinitionoftheconvexhullalgorithm withthreeparameterstodoso.


template <class BidirectionalIterator, class OutputIterator> OutputIterator convex_hull( BidirectionalIterator first, BidirectionalIterator beyond, OutputIterator result) { typedef typename iterator_traits<BidirectionalIterator>::value_type P; typedef typename P::Number_type Number_type; typedef Convex_hull_traits<Number_type> Traits; return convex_hull( first, beyond, result, Traits());

file://C:\Users\rmanoj\Desktop\Untitled.htm

1/13/2010

Page 50 of 72

Onebenefitofusingfunctionobjectsinthetraitsclassinsteadofplainmemberfunctionsisthepossibleassociationofastatewiththe functionobject.Weextendthistoatraitsclasswithastate.

In our framework on one sided error predicates [Kettner98b] we introduced the notion of a conservative implementation of a predicate. If a conservative implementation of the leftturn predicate returns true, the three points perform a left turn, but if it returns false, we do not know the orientation of the points. Thus, the decision errors due to rounding errors in inexact arithmetic are limited to one side of the two possible answers. Useful applications for such predicates will assume that these false answers occur rarely, but in principle an implementation saying always false is a legal implementation. Examples for convex hull algorithms and a triangulation of point sets are given in the paper that compute a well defined output if the predicate is not exact but a conservative implementation. Andrew's variant of Graham's scan as presented above computes a sequence of points of which the points on the convex hull are a subsequence if it is used with a conservative implementation of the predicate. The output can be easily postprocessed with the same algorithm but with an exact implementation of the predicate. For more properties of the computed output and other examples see [Kettner98b]. For floating point arithmetic an error bound can be computed such that if the expression computing the determinant is larger than the error bound, the exact value of the determinant is greater than zero. The expression to compute the determinant and its error bound give us the conservative implementation
(q.x-p.x)*(r.y-p.y) - (q.y-p.y)*(r.x-p.x)

> 8(3u + 6u2 + 4u3 + u4) B2 ,

where u is the unit roundoff of the floating-point number system and B is the absolute value of the maximal coordinate value of the points. We have u = 2-53 for IEEE double precision, and u=2-24 for IEEE single precision floating-point numbers. In the following traits class we assume a built-in type double following the IEEE standard and we make the value B a state value of the traits class. In order to make the error bound representable as double, we round it to (3 2-50 + 2-100) B2 and require B to be a power of two.
class Convex_hull_traits_2 { double B; public: typedef Point_2<double> Point; Convex_hull_traits_2( double b) : B(b) {} struct Leftturn { double B; Leftturn( double b) : B(b) {} bool operator()( const Point& p, const Point& q, const Point& r) { const double C = 1.0 / 1024.0 / 1048576.0 / 1048576.0; //2^-50 return (q.x-p.x) * (r.y-p.y) - (r.x-p.x) * (q.y-p.y) > (3.0 * C + C * C) * B * B; } }; Leftturn leftturn() const { return Leftturn(B); } };

Justtoshowaspecializationofaclasstemplate,thefollowingdefinitionreplacesthegenerictraits,whichassumes,withourspecialized conservativepredicatetraitsforthenumbertypedouble.Inthisimplementation,Bisarbitrarilysettoone.
template <> struct Convex_hull_traits<double> : public Convex_hull_traits_2 { Convex_hull_traits() : Convex_hull_traits_2(1.0) {} };

SinceBisprobablyaconstantparameter,itmightbemoreappropriatetomakeitatemplateparameterinConvex_hull_traits_2. However,anotherexampleofausefultraitsclasswithastateisthecomputationofthetwo-dimensionalconvexhullforasetofthreedimensionalpointsprojectedontoatwo-dimensionalplane.Insteadofprojectingthepointsandcomputingtheconvexhullontheprojected points,theconvexhullcanbecomputedwiththeoriginalthree-dimensionalpointsandamodifiedleftturnpredicatethattakesinto accounttheprojectionstoredasalocalstate.

Another example of the flexibility of the geometric traits classes is the reconstruction of a terrain from a set of three-dimensional sample points. A common approach is to triangulate the sample points using a Delaunay triangulation in the xy-projection, just ignoring the elevation in the z-coordinate. Similar to the convex hull algorithm, a geometric class can be used to parameterize the two-dimensional triangulation algorithm to work on the three-dimensional data set. See the example for CGAL in the previous Section.

7. Design Patterns
Introduction
The term design patterns has been introduced to a broad audience of object-oriented developers with the book with the same title [Gamma95]. Design patterns capture the essence of a design solution that has been proven to be useful in practice. Patterns are not invented, patterns are discovered in existing systems. A pattern has to show up in different systems before it is considered to be useful in practice. A pattern consists of:

file://C:\Users\rmanoj\Desktop\Untitled.htm

1/13/2010

Page 51 of 72

Name suchasAdapter,Observer,Singleton... Problem theproblemstatement,itscontextandpreconditions. Solution classes,objects,theirrelationship,responsibilities,andcollaborations. Consequences results,tradeoffs.

Further information on patterns in the net:


Appleton:Essentialconcepts:http://www.cmcrossroads.com/bradapp/docs/patterns-intro.html PatternsHomepage:http://hillside.net/patterns/ WUSTLPatternspage:http://www.cs.wustl.edu/~schmidt/patterns.html

Abstract Factory Pattern


Problem: Consider an application using different graphical user interfaces (GUI) with widgets of different look and feels. The application should be seperated from the decision about the look and feel of its graphical user interface. Solution: Instead of creating widgets directly, we ask an object -- the factory -- to create them for us. Each widget has its own abstract base class and concrete implementations according to the different GUIs.
class Window { virtual ~Window(){} }; class Motif_window : public Window {}; class Athena_window : public Window {}; class Button { virtual ~Button(){} }; class Motif_button : public Button {}; class Athena_button : public Button {};

The factory itself is also implemented as an abstract base class and a concrete class for each GUI. The abstract base class defines an interface with a member function to create each widget. The concrete factory classes create the widgets of the respective GUI.
class Widget_factory { virtual Window* create_window() = 0; virtual Button* create_button() = 0; virtual ~Widget_factory(){} }; class Motif_widget_factory { virtual Window* create_window() virtual Button* create_button() }; class Athena_widget_factory { virtual Window* create_window() virtual Button* create_button() };

{ return new Motif_window; } { return new Motif_button; }

{ return new Athena_window; } { return new Athena_button; }

The application uses only pointers to the abstract base classes of the various widgets. It uses a pointer to a factory to create concrete instances of widgets. Consequences: A factory isolates concrete classes. It makes exchanging a whole family of classes easy. It promotes consistency among the family. New kinds of widgets are dificult to add.

Singleton Pattern
Problem: Ensure a class only has one instance, and provide a global point of access to it. An example are classes representing unique resources in a system, or a printer spooler, a window manager, etc. Solution: The singleton class has only private constructors that forbid the creation of any objects outside of the class. A static member function is used as a global point of access. It returns a pointer to the unique instance of the singleton that is stored internally in a static member variable.
class Singleton { Singleton(); Singleton( const Singleton&); static Singleton* instance; public: static Singleton* Instance(); };

file://C:\Users\rmanoj\Desktop\Untitled.htm

1/13/2010

Page 52 of 72

Singleton* Singleton::instance = 0; Singleton* Singleton::Instance() { if ( instance == 0) instance = new Singleton; return instance; }

Consequences: controlled access, reduced namespace, and permits other limitation on the number of instances.

Adaptor Pattern (a.k.a. Wrapper Pattern)


Problem: A class C provides the right functionality, but not the correct interface, e.g., it is not derived from a required base class. Solution: Write another class A with the correct interface that contains an instance of class C. Class A implements its interface by calling the right functions in its object of class C. This pattern is so simple that I omit an example here. The boundary is blurred between an adaptor pattern and a class using some other class to implement its functionality. An adaptor does not perform the major part of the task itself. Consequences: Helps if derivation from a required base class is missing (different libraries etc.). Same for sets of template requirements (see adaptors in the STL). Tradeoffs, how much work has the adaptor to do?

Visitor Pattern
Problem: Given a collection of objects, the objects are of types from a class hierarchy, and a set of operations that operate on these objects. The visitor pattern allows to define new operations without changing the classes of the elements on which it operates. Solution: First, the canonical solution that is not extendible. Let us assume a scene graph API with different node types Transform and Geometry, both derived from the abstract base class Node. Let us define two operations, render and optimize, which we make virtual member functions of Node.
struct Node { virtual void render() = 0; virtual void optimize() = 0; virtual ~Node() {} }; struct Transform : public Node { virtual void render(); virtual void optimize(); }; struct Geometry : public Node { virtual void render(); virtual void optimize(); };

This solution is easy to extend with new subclasses of Node, but it is hard to extend with new operations. Now, the visitor pattern factors out the operations in a seperate hierarchy of classes, all derived from the virtual base class Visitor and each derived class representing one operation. The Visitor classes have a virtual member function for each type in the Node class hierarchy, which performs the operation on that type. We also change the Node hierarchy to accept an object of type Visitor and to call the member function that corresponds to the actual type of the node object. The application now takes an operation object and calls accept of each node object with it.
struct Transform; // forward declaration struct Geometry; // forward declaration struct Visitor { virtual void visit_transform( virtual void visit_geometry ( virtual ~Visitor() {} }; struct Render : public Visit { virtual void visit_transform( virtual void visit_geometry ( }; struct Optimize : public Visit { virtual void visit_transform( virtual void visit_geometry ( };

Transform* ) = 0; Geometry* ) = 0;

Transform* ); // render Transform Geometry* ); // render Geometry

Transform* ); // optimize Transform Geometry* ); // optimize Geometry

file://C:\Users\rmanoj\Desktop\Untitled.htm

1/13/2010

Page 53 of 72

struct Node { virtual void accept( Visitor* ) = 0; virtual ~Node() {} }; struct Transform : public Node { virtual void accept( Visitor* v) { v->visit_transform( this); } }; struct Geometry : public Node { virtual void accept( Visitor* v) { v->visit_geometry( this); } };

Consequences: The visitor pattern is easy to extend with new operations in the Visitor hierarchy, but it is hard to extend with new object types. The visitor pattern groups related operations together. Visitors can be used to accumulate state. Visitors might require to break some encapsulation (expose more implementation details of the objects), since the operation is no longer a member function of the object.

CGAL::Object, a Polymorphic Type in CGAL


Problem: We need a polymorphic return type for intersections in CGAL, for example, the intersection of two segments can be either emtpy, a point, or a segment. But we do not want a single global base class and class hierarchy for all classes in CGAL. Here the example of segment intersection in CGAL:
Segment s( Point(1,1), Point(1,5)); Segment t( Point(1,3), Point(1,8)); CGAL::Object result = CGAL::intersection( s, t); Point pt; if (CGAL::assign( pt, result)) cout << "intersection point = " << pt << endl;

Solution: We do not impose any restriction on CGAL classes. Instead, we create an addtional class hierarchy only where we need it, here for the polymorphic return type (see also [Weihe98]).
class Base { // base class for wrapper classes public: virtual ~Base() {} }; template <class T> // generic wrapper class class Wrapper : public Base { T object; public: Wrapper(const T& obj) : object(obj) {} Wrapper() {} operator T() { return object; } }; class Object { // polymorphic object (smart pointer) Base* p; public: // ... Base* base() const { return p; } };

The assign function uses runtime type information to check whether the object stored in the wrapper behind CGAL::Object is of the appropriate type to actually copy it.
template <class T> bool assign(T& t, const Object& o) { Wrapper<T>* wp = dynamic_cast<Wrapper<T>*>(o.base()); if (wp == 0) return false; t = *wp; return true; }

Consequences: The original CGAL classes are not influenced by the design decision for intersection computation (locality of this design decision). They do not carry the extra vptr and associated costs for RTTI (runtime type information). Remark: In CGAL, the design is a bit more involved since it exploits the handle/rep scheme with reference counting of CGAL to avoid the cost of copying the types into and out of the wrapper class.

8. Template Metaprograms
Introduction

file://C:\Users\rmanoj\Desktop\Untitled.htm

1/13/2010

Page 54 of 72

TemplatemetaprogrammingreferstoatechniquewherethetemplateinstantiationmechanismoftheC++compilerisusedtopartially evaluateaprogramatcompiletime.AtsomepointduringthestandardizationprocessofC++ithasbeendiscoveredthattemplatesmakethe C++compileractuallyTuringequivalentatcompiletime.ThefirstexamplesusingtheC++compilerasaninterpreterarecreditedtoErwin Unruh.Amongthemthefollowingprogramthatcomputesprimenumbersatcompiletime.Theresultiscommunicatedwithcompiler warningscontainingtheprimenumbers.ThisprogramcirculatedamongthemembersoftheANSI/ISOC++standardizationcommittee.The programdoesnotworkanymoreoncurrentcompilers,seeprime.Cforamodifiedversionthatcompilesaspromisedonthecurrentg++ compiler.


// prime.C // Program by Erwin Unruh template <int i> struct D { D(void*); operator int(); }; template <int p, int i> struct is_prime { enum { prim = ((p%i) && is_prime< (i>2 ? p : 0), i-1>::prim) }; }; template <int i> struct Prime_print { Prime_print<i-1> a; enum { prim = is_prime<i,i-1>::prim }; void f() { D<i> d = prim; } }; struct is_prime<0,0> { enum { prim = 1}; }; struct is_prime<0,1> { enum { prim = 1}; }; struct Prime_print<2> { enum { prim = 1}; void f() { D<2> d = prim; } }; void foo() { Prime_print<20> a; }

ToddVeldhuizenusestheseadvancedtemplatetechniqueswithtemplatemetaprograms[Veldhuizen95a]andwithexpressiontemplates [Veldhuizen95b],whichwearegoingtodetaillater.

In [Veldhuizen95a] a template metaprogram for bubblesort is described. Instantiated for a constant number of elements, the metaprogram unrolls the loops of bubblesort and creates the decision tree to sort the elements. The second example is a compile time function for sinus and cosinus that can be used to implement a metaprogram for the FFT, resulting in a single unrolled function for a 256 FFT with all roots evaluated as constants. In [Veldhuizen95b] templates are used to write code such as:
// Integrate a function from 0 to 10 DoublePlaceholder x; double result = integrate( x / (1.0 + x), 0.0, 10.0); Thetermx / (1.0 + x)isatemplateexpression.Itsbenefitisthatitcanbeexpandedinlineintheintegratefunction.Template

expressionshavealsonicersyntaxthanfunctors.Thesametechniqueisusedinthepapertoimplementvectorarithmeticmoreefficiently, namelyavoidinglargetemporaryresultsinvectorexpressions.

Both techniques are used in Blitz++, a library for numerical algorithms with vectors and matrices, see http://oonumerics.org/blitz/.

Compile-time programming
TheC++languageprovidestwoentitieswhichcanbeusedtoprogramatcompiletime: types constantintegralvalues Wenowdescribealistofbasicoperationsontheseentites,whichcanbeusedtoprogram"atcompiletime".Letusbeginbymakingan analogywiththefollowingusualfunction:
int f(T t) { return t.category(); } int a = f(t); // function (f) : associates a value (a) to an object (t)

Nowobservethesimilaritywiththefollowingfunctionontypes:
template < typename T > struct F { typedef typename T::Category type;

file://C:\Users\rmanoj\Desktop\Untitled.htm

1/13/2010

Page 55 of 72

}; typedef F<T>::type A; // function (F) : associates a type (A) to the type (T)

Constantintegralvaluescanbeassociatedtotypesandvice-versa,atcompiletime.
template < typename T > struct G { enum { value = 3 }; }; template < int i > struct H { typedef H<i+1> type; }; template < int i > struct K { enum { value = i+1 }; }; int a = G<T>::value; // (G) : associates an integral value (a) to the type (T) typedef H<2>::type A; // (H) : associates a type (A) to an integral constant value (2) int a = K<2>::value; // (K) : associates an integral value (a) to an integral constant value (2)

Ofcourse,such"meta"functionsarenotrestrictedtoonlyoneargument,andnoteventoonereturnentity.
template < int i, typename T, int j, typename U > struct Z { enum { value1 = i+1 }; enum { value2 = j+i }; typedef Y<T, U> type; }; Built-inoperators(+, -, *, /, %, ?:, ^, !, &, |, &&, ||, <<, >>, <, >, <=, >=, ==, !=)canalsobeusedtogenerate

constantintegralvaluesdirectly:
int a = K<2+3>::value; // (+) : associates an integral value (5) to two integral constant values (2 and 3)

Wecanapplycompositionofmetafunctions:
int a = G<F<2>::value>::value; // Composition of F and G.

Todorealprogramming,weneedtobeabletoperformbranches.Thiscanbeachievedusing(eventuallypartial)specialization.
template < int i > struct D { enum { value = D<i-1>::value }; }; template <> struct D<0> { enum { value = 0 }; }; // Which is equivalent to the run-time program : int d(int i) { if (i == 0) return 0; return d(i-1); } // Note that the following works as well in this particular case : template < int i > struct E { enum { value = i==0 ? 0 : D<i-1>::value }; }; Theis_primeexampleintheintroductionillustratessomemorepossibilities.

Another tool, which allows to connect compile-time programming to the powerful function overload resolution mechanism of C++, is the sizeof() operator :
struct LargeStruct { char c[2]; }; char f(...); LargeStruct f(double); template < int s > struct Is_double { enum { value = (s == 1) }; }; // x is some expression, e.g. "std::sqrt(2.0)" int a = Is_double< sizeof(f(x)) >::value; Notethatsizeof()returnsanintegralconstantvalue.Italsohasthepropertythattheexpressiontowhichitisappliedneedsnotbedefined

(beingdeclaredisenough).Overloadresolutionisacomplexmechanism,andwewillseelaterhowitallowstoextractsomepropertiesof typesautomatically.

Constraining templates using the SFINAE principle

file://C:\Users\rmanoj\Desktop\Untitled.htm

1/13/2010

Page 56 of 72

InC++,overloadresolutionistheprocessbywhichthecompilerselectswhichspecificfunctiontouseinagivencall,whenthatfunctionis overloaded.Considerthefollowingoverloadedfunctionf:
void void void void f(); f(int); f(double); f(...);

template < typename T > void f(T const&); int main () { float x; f(x); // which function f is going to be called ? }

Thecompilerproceedsin2stages:itfirstselectsallfunctionsthatcouldmatchtheargument(inthiscase,thelast3declarations),thenit discardsthosewhichrequireaconversionwhichhastheleast"priority".Ifthereisnotoneuniquebestmatch,thenthisambiguouscaseleads toanerror.Soconversionrulescomeintoplay,andthisisanareaofC++whichisverycomplicated.Inthiscase,thetemplateversionisgoing tobechosenbecausethereisnoperfectmatchotherwise,theotherpossibilitiesrequireconversionswhichhavelesspriority,andtheellipses versionistheonewiththeleastpriority.

There is a particular feature of the first processing stage performed by the compiler, when it tries to substitute the types of the actual arguments of the function call into each function declaration : this process does not produce an error if it fails, it just discards the considered function from the set of possible matching functions. This is called the "Substitution Failure Is Not An Error" (SFINAE) principle. Now let's add the following additional overloaded function to the previous program :
template < typename T > struct G {}; template < typename T > void f(T const&, typename G<T>::type = 0); GiventhatGdoesnotdefineanestedtypetype,tryingtosubstitutefloatinthisoverloadedfunctionisgoingtofail.TheSFINAEprinciple

impliesthattheprogramisperfectlyvalid,andthatthefirsttemplateisstillchosen.

Now what happens if we define a nested type type in G ?


template < typename T > struct G { typedef int type; };

Thenthesecondtemplatematchesaswell(thereisnofailureduringthesubstitutionprocess),andwegetanambiguityerrorwiththefirst template,becauseitmatcheswithequalpriority.

Now this opens new horizons, because the template class G can be specialized for some particular types, and can define or not a nested type type. This allows to perform some selection on the templates, to constrain them depending on some properties on types. However, the process is not yet so nice, because it requires an additional argument to the function (although we can specify a default value), and this is impossible for overloaded operators. The remark that is going to save this idea for operators is that the SFINAE principle applies to the whole function signature, including the return type.
template < typename T > typename G<T>::type f(T const&);

Letusnowhavealookathowwecouldmakeuseofthisinapracticalcase:theCGALlibrary.There,wewouldliketodefinegenericfunctions foraddinggeometricvectors:
template < typename Vector_2 > Vector_2 operator+(Vector_2 const &v, Vector_2 const & w) { return Vector_2(v.x() + w.x(), v.y() + w.y()); }

Obviously,thisdoesn'twork,becausethistemplateiswaytoogeneral(itmatchesalmostanytype),anditwouldclashwith,forexample:
template < typename Vector_3 > Vector_3 operator+(Vector_3 const &v, Vector_3 const & w) { return Vector_3(v.x() + w.x(), v.y() + w.y(), v.z() + w.z()); }

So,let'sconsiderconcretetypesprovidedbytheuser,towhichwewouldliketoapplyourgenericoperator:
class My_vector { int _x, _y; public: // 2D vector type

My_vector(int x, int y) : _x(x), _y(y) {} int x() const { return _x; } int y() const { return _y; } }; class My_point { int _x, _y; public: // 2D point type

file://C:\Users\rmanoj\Desktop\Untitled.htm

1/13/2010

Page 57 of 72

My_point(int x, int y) : _x(x), _y(y) {} int x() const { return _x; } int y() const { return _y; } };

ThetypesMy_vectorandMy_pointhaveidenticalinterfaces,andwewouldlikethatourgenericoperatorappliesonlyonMy_vector.

So the idea is to constrain the function template, in such a way that it will be considered only for types that are 2D geometric vector types, and which provide the needed requirements (.x() and .y() member functions, correct constructor, adequate semantics...). This kind of thing is typically a job for a traits class.
template < typename T > struct IsVector_2 { enum { value = false }; };

// By default, a type is not a 2D vector.

template <> struct IsVector_2 <My_vector> { enum { value = true }; // But My_vector is a 2D vector. };

Sonowwecaneasilyconstrainthetemplateusinganotheraccessorytool:
template < typename T, typename, bool = T::value > struct Enable_if; template < typename T, typename R > struct Enable_if <T, R, true> { typedef R type; }; template < typename Vector_2 > typename Enable_if< IsVector_2<Vector_2>, Vector_2 >::type operator+(Vector_2 const &v, Vector_2 const &w) { return Vector_2(v.x() + w.x(), v.y() + w.y()); }

Thefollowingmainfunctionillustrateswhatwefinallyget:
int main() { My_vector v(1,2), w(3,4); My_vector z = v + w; // OK My_point p(1,2), q(3,4); My_point r = p + q; // error : // no match for `My_point& + My_point&' operator }

Thewholeprogramcanbefoundhere:vector_2.C.

Type Classification
(Note:thissectionisinspiredbychapter19of[Vandevoorde03])

We have just seen an example of traits class to express a property of a type. We are now going to see ways to write traits classes to automatically extract fundamental properties of types. Let's start by writing a traits class which determines if a type is a fundamental type or not. Given that there is a small finite number of such types, it is easy to enumerate them :
template < typename > struct IsFundamental { enum { value = false }; }; template <> struct IsFundamental<int> template <> struct IsFundamental<short>

{ enum { value = true }; };

{ enum { value = true }; };

template <> struct IsFundamental<double> { enum { value = true }; }; // similarly for bool, char, long, long double and the unsigned versions.

Nowlet'strytoclassifycompoundtypes,thatis,typeswhichareconstructedfromothertypes:plainpointers,references,arrays...Wecanuse partialspecializationtoidentifysomeofthesecategories.Inthiscase,itisalsousefultoextractwhichtype(s)thecompoundismadeof(e.g. thetypeofeachitemforanarray).


// Primary template template < typename T > struct CompoundType { enum { IsPointer = false, IsReference = false, IsArray = false, IsFunction = false, IsPointerToMember = false };

file://C:\Users\rmanoj\Desktop\Untitled.htm

1/13/2010

Page 58 of 72

typedef T base_type; }; // Partial specialization for references template < typename T > struct CompoundType<T&> { enum { IsPointer = false, IsReference = true, IsArray = false, IsFunction = false, IsPointerToMember = false }; typedef T base_type; }; // Partial specialization for pointers template < typename T > struct CompoundType<T&> { enum { IsPointer = true, IsReference = false, IsArray = false, IsFunction = false, IsPointerToMember = false }; typedef T base_type; }; // Partial specialization for arrays template < typename T, size_t N > struct CompoundType<T[N]> { enum { IsPointer = false, IsReference = false, IsArray = true, IsFunction = false, IsPointerToMember = false }; typedef T base_type; }; // Partial specialization for empty arrays template < typename T > struct CompoundType<T[]> { enum { IsPointer = false, IsReference = false, IsArray = true, IsFunction = false, IsPointerToMember = false }; typedef T base_type; }; // Partial specialization for pointers to members template < typename T, typename C > struct CompoundType<T C::*> { enum { IsPointer = false, IsReference = false, IsArray = false, IsFunction = false, IsPointerToMember = true }; typedef T base_type; };

Unfortunately,functiontypescannotberecognizedthateasily.WearethereforegoingtousetheSFINAEprinciple,aftermakingthefollowing remark:theonlytypeswhichcannotbegatheredinanarrayarefunctiontypes,voidandreferences.Wenowexploitthisfactbytryingto makeanarrayofthegiventype,andifitfails,itmeansthetypeisoneofthesespecialtypes."Trying",here,isasynonymforusingtheSFINAE principle.


template < typename T > class IsFunction { struct LargeStruct { char c[2]; }; template < typename U > static char test(...); template < typename U > static LargeStruct test(U (*)[1]); public: enum { value = sizeof( IsFunction<T>::test<T>(0) ) == 1 }; }; template < typename T > struct IsFunction<T&> { enum { value = false }; }; template <> struct IsFunction<void> { enum { value = false }; }; template <> struct IsFunction<void const> { enum { value = false }; };

Howtodetermineenumeratedtypes?Whatarethefeaturesofthistypesthatwecouldexploittodetectthem?Weknowthattheyare convertibletointegraltypes.Butweneedsomethingmore,becauseauserdefinedclasscouldalsodefineaconversiontoanintegraltype.

We are not going to describe the full code here, but the curious reader can find the solution in [Vandevoorde03]. Hint : two user defined conversions cannot be applied automatically consecutively, whereas type promotion between enums and integral types do not have this restriction. To conclude : it is possible to write traits classes that determine almost all basic properties of types. Libraries such as Boost provide such mechanisms in an extensive way, and the next revision of the standard library may contain such a type_traits mechanism. However, there are properties which cannot be determined using the current language : it is possible to determine if a class defines a static data member of a given name using SFINAE (static_member_detection.C), but it is not possible to extract the list of all data members of a class. Such a possibility would require changes in the core language. The following section illustrates what could be

file://C:\Users\rmanoj\Desktop\Untitled.htm

1/13/2010

Page 59 of 72

done with such a feature.

Self Reflection of Types in C++: The flatten.C Example


InthissectionIdescribehowfaronecouldgowithgenericalgorithmsiftypeswouldbefirstclasscitizensinC++.

The following is a correct program in the functional programming language PolyP (see http://www.cs.chalmers.se/~patrikj/poly/polyp/):
module Main where import Flatten(flatten) data Foo a = Bar | Cee a | Doo (Foo a) a (Foo a) x = Doo (Doo (Cee 'P') 'a' (Doo Bar 't' Bar)) 'r' (Doo Bar 'i' (Doo Bar 'k' Bar)) main = print (flatten "Patrik", flatten x) -- expected output: ("Patrik", "Patrik") Foo aisatypicalparameterizedrecursivetypedefinition,hereabinarytree,wherethenodeswithconstructorDooandtheleaveswith constructorCeecontainanelementoftypea,whiletheleaveswithconstructorBarareempty.Thevariablexcontainssuchatree.The genericfunction(calledpolytypicinPolyP)flattenperformsanin-ordertraversalandconcatenatesallelementsoftypeaitcanfind.

The trick is the implementation of flatten without knowing the structure of Foo. Instead, PolyP allows to write functions over the structure of how types can be defined in the language in general. There is only a small set of possebilities how types can be defined in PolyP, for example, alternatives of types or Cartesian product of types. Can we write such a flatten function in C++? No, we cannot, but almost. The missing information is what could be called selfinspection of types. We need meta information of a type how it is constructed. For example, for a struct we need to know its member types. Let us assume we would have such information, and for the running example we provide this information just by hand (as a clever annotation to the type definition). Then, we could write the following program in C++ (see flatten.C for the full program):
// ----------------------------------struct B; struct A { int i; A* a; B* b; }; struct B { A* a; }; // ----------------------------------int main() { A a1; a1.i = 1; A a2; a2.i = 2; A a3; a3.i = 3; B b; a1.a = &a2; a2.a = &a3; a3.a = 0; a1.b = &b; a2.b = 0; a3.b = 0; b.a = &a2; list<int> ls; flatten(a1,ls); copy( ls.begin(), ls.end(), ostream_iterator<int>(cout, " ")); cout << endl; } EXAMPLE MAIN PROGRAM USER DATA TYPES

Theprogramcreatesanacyclicgraphrootedata1.NodesoftypeAcanpointtonodesoftypeAandtonodesoftypeB.Theyalsocontainan integervalue,thevaluewewanttoconcatenateduringourflattencall.NodesoftypeBcanjustpointtoanodeoftypeA.Thegraphlooks asfollows,nodesarelabelledwiththeirtypes,andnodesoftypeAcontaintheirintegervalue:

file://C:\Users\rmanoj\Desktop\Untitled.htm

1/13/2010

Page 60 of 72

The return value of the flatten call is stored in the list ls. The output of the program is the sequence [1,2,3,2,3]. We have to add some annotation for our type definitions of A and B to make this example work. For the flatten function, we can get away with a rather dense notation that gives us for each structure the types of the members that it contains (incl. how many), and a way to access the different members. We define a class template with two arguments. The first argument is the type we want to annotate. The second argument is the cardinal number of the current member we are describing. The general class template has an operator() that just returns an object of type UNKNOWN. For each member in each type we write a specialization of the class template. The specialization has an operator() that, given an object of that type, returns the member of the given cardinal number.
// ----------------------------------struct UNKNOWN {}; ANNOTATE TYPES FOR SELF-INSPECTION

template <class X, int i> struct T { UNKNOWN operator()( X&) { return UNKNOWN();} }; template <> struct T<A,1> { template <> struct T<A,2> { template <> struct T<A,3> { int A* B* operator()(A& a) { return a.i; } }; operator()(A& a) { return a.a; } }; operator()(A& a) { return a.b; } };

template <> struct T<B,1> { A* operator()(B& b) { return b.a; } }; TheflattenfunctionunrollsalongthewaytypescanbedefinedinC++.Wehaverestrictedthisexampletodealwithpointersandstructures. Wehaveomitted,forexample,referencetypes,unions,andarrays.Wefollowtheflattenfunctioncall.First,wedistinguishbetweenpointer

typesandnon-pointertypes.Weusepartialspecializationofaclasstemplatetoimplementthisdistinction.Forpointertypes,weapplyflatten recursivelyonthedereferencedvalueofthepointer.
// ----------------------------------- VALUE / POINTER template <class X, class I> struct flatten_class { void operator()( X x, list<I>& ls) { flatten_items( T<X,1>()(x), T<X,1>(), x, ls); } }; template <class P, class I> struct flatten_class<P*,I> { void operator()( P* x, list<I>& ls) { if ( x != NULL) flatten_class<P,I>()(*x, ls); } }; template <class X, class I> void flatten( X x, list<I>& ls) { flatten_class<X,I>()( x, ls); }

Second,wedistinguishbetweenstructsandsimpletypes.Forstructs,weiteratethroughallmembersofthestructandcallflatten recursively.Ifthetypeissimpleandequaltothevaluetypeofthelist,weappendthevaluetothelist.Thus,inthisimplementationofthe flattenfunction,thevaluetypeofthelistactuallydetermineswhatgetscollected.Weuseagainaclasstemplateandpartialspecialization todothematch.NotehowtheUNKNOWNreturntypecausestherecursiveenumerationofallthememberstostop.Notealsothatinthecase wewanttoflattenastructthathasametadescriptionwegetanambiguousmatchbetweenthesecondandfourthspecialization.Thethird specializationsolvesthisambiguity.


// ----------------------------------- LEAF / STRUCT WITH SUBITEMS template <class Item, class X, int n, class I> struct flatten_items_class { // flatten all subitems void operator()( Item i, X x, list<I>& ls) { flatten( i, ls); flatten_items( T<X,n+1>()(x), T<X,n+1>(), x, ls); } }; template <class Item,int n, class I> struct flatten_items_class<Item,I,n,I> { // item of type I found void operator()( Item, I i, list<I>& ls) { ls.push_back(i); } }; template <int n, class I> // item of type I found struct flatten_items_class<UNKNOWN,I,n,I> { void operator()( UNKNOWN, I i, list<I>& ls) { ls.push_back(i); } }; template <class X, int n, class I> // no (further) subitems

file://C:\Users\rmanoj\Desktop\Untitled.htm

1/13/2010

Page 61 of 72

struct flatten_items_class<UNKNOWN,X,n,I> { void operator()( UNKNOWN, X x, list<I>& ls) {} }; template <class Item, class X, int n, class I> void flatten_items( Item i, T<X,n>, X x, list<I>& ls) { flatten_items_class<Item,X,n,I>()(i,x,ls); }

Thisconcludesourpresentationofflatten.C.ThepurposeofthisexamplewastostretchtheimaginationwithwhatwouldbepossibleinC++ (andispossibleinotherlanguages),buthowuglyandunmaintainableitlooksinC++.

Expression Templates
(Note:thissectionisinspiredbychapter18of[Vandevoorde03])

Expression templates have several uses, but basically the advantages of this technic are :
itallowstowritecomplextypesinanaturalmanner.Thesetypesrepresentexpressions. itcanbeappliedtoimplementproblemspecificoptimizationsautomatically,allowingtoreachperformancethatonlyhand-codedlow levelcodecouldhavereached,byrephrasingsomeexpressionsatcompiletimefromahighleveldescription. Thetypicalexampleofexpressiontemplatesistheoptimizationofarrayoperations.Let'sconsider:
class SArray { double * _a; size_t _s; public: SArray (size_t n) : _a(new double[n]), _s(n) {} double const& operator[](size_t i) const { return _a[i]; } double & operator[](size_t i) { return _a[i]; } size_t size() const { return _s; } ~SArray() { delete[] _a; } }; SArray operator+(SArray const& a, SArray const& b) { assert(a.size() == b.size()); SArray tmp(a.size(); for (int i = 0; i < a.size(); ++i) tmp[i] = a[i] + b[i]; return tmp; }

Now,ifweconsiderthetypicalusebelow,wecanseethatthereisanefficiencyproblemwiththelastline:atemporaryarrayiscreatedin ordertostoretheresultofx + y.Thisdoesnotincreasethenumberofadditionstobeperformed,butitincreasestheamountofmemory requiredbytheprogram,whichmakesitslowerduetocacheeffects.


SArray x, y, z, t; ... // do something useful with x, y, z t = x + y + z;

Wecansolvetheproblembyusingadedicatedfunctionthataddsthreearraysinoneshot:
SArray add_3(SArray const& a, SArray const& b, SArray const& c) { assert(a.size() == b.size() && a.size() == c.size()); SArray tmp(a.size(); for (int i = 0; i < a.size(); ++i) tmp[i] = a[i] + b[i] + c[i]; return tmp; } ... t = add_3(x, y, z);

Thisisinconvenientbecausethenotationiscumbersome,andforanyexpressionbasedonarrays,wewouldliketobenefitfromthe optimization,withoutrequiringtowriteanewfunctionfor,e.g.x+y*z-z*x,orwhatevertheuserofourarrayclasswantstocompute.Thisis whereexpressiontemplatescomeintoplay.

The idea is to postpone the actual evaluation of the expression until the assignment operator is seen. This is done by creating an object that will encode the expression in the form of a tree :

file://C:\Users\rmanoj\Desktop\Untitled.htm

1/13/2010

Page 62 of 72

Each node in this tree has a particular type representing the type of operation (e.g. +) that it represents, together with references to its operands.
template < typename OP1, typename OP2 > class A_Add { OP1 const& op1; // first operand OP2 const& op2; // second operand public: A_Add (OP1 const& a, OP2 const& b) : op1(a), op2(b) {} // What it is contributing in the final computation double operator[] (size_t i) const { return op1[i] + op2[i]; } };

Noticenowhowoperator+isgoingtocreateanodeoftheexpressiontree,andnocomputationonthearraysatall.Wefirstneedtowritea wrapperaroundtheconcretearraySArray.Thisistheassignmentoperatorwhichtriggerstherecursivecomputationoverthetree.
template < typename Rep = SArray > class Array { Rep expr_rep; public: // initialization with a size explicit Array(size_t s) : expr_rep(s) {} // create an array from a possible representation Array (Rep const& rb) : expr_rep(rb) {} // Assignment of arrays of the same type Array& operator=(Array const& b) { assert(size() == b.size()); for (size_t i = 0; i < b.size(); ++i) expr_rep[i] = b[i]; return *this; } // Assignment of arrays of the different type template < typename Rep2 > Array& operator=(Array<Rep2> const& b) { assert(size() == b.size()); for (size_t i = 0; i < b.size(); ++i) expr_rep[i] = b[i]; return *this; } double operator[] (size_t i) { assert(i < size()); return expr_rep[i]; } Rep const& rep() const { return expr_rep; } Rep & rep() { return expr_rep; } }; template < typename R1, typename R2 > Array<A_Add<R1, R2> > operator+(Array<R1> const& a, Array<R2> const& b) { return Array<A_Add<R1, R2> > (A_Add<R1, R2>(a.rep(), b.rep())); }

Similarlyfortheotheroperations:
template < typename OP1, typename OP2 >

file://C:\Users\rmanoj\Desktop\Untitled.htm

1/13/2010

Page 63 of 72

class A_Mul { OP1 const& op1; // first operand OP2 const& op2; // second operand public: A_Mul (OP1 const& a, OP2 const& b) : op1(a), op2(b) {} double operator[] (size_t i) const { return op1[i] * op2[i]; } }; template < typename R1, typename R2 > Array<A_Mul<R1, R2> > operator*(Array<R1> const& a, Array<R2> const& b) { return Array<A_Mul<R1, R2> > (A_Mul<R1, R2>(a.rep(), b.rep())); }

Withthisimplementation,wehaveachievedwhatwehavepromised.Notethatacompleteimplementationwouldalsohavetotakecareof somecornercaseslikeproperhandlingoflocalvariables(bycopyingtheminthetreenodes,notreferencingthem),alsopayingattentionto caseswherethevariablebeingassignedtoisalsointhearguments(e.g.x = y + x)...

Caveats : for the optimizations to apply, the compiler needs to perform two kinds of particular optimizations itself : inlining all the small functions which locally create a tree on the stack, and split the tree node structures in small components. For example, the latter is not being performed by g++ at the moment (version 3.3). As with some other template technics, it also has the potential to increase compilation times considerably depending on the program. Another caveat of the expression templates is a bad interaction with generic libraries. Consider :
template < typename T > T square(T const& t) { return t*t; } Array<> x, y, z; ... z = square(x); z = square(x+y);

// OK // error Theproblemhereisthatx+ydoesn'thavethe"base"typeArray<>,butithasthetypeofatreenode.Insidethesquarefunction,thereturn

valueisthereforegoingtobethatparticulartype,butthereisnopossibleconversionbetweenanytwodifferent"internal"treenodetypes. Theonlypossibleconversionisfromaninternaltreenodetothe"base"typeArray<>.Thereforeithastobeexplicitelyconvertedbeforethe calltothesquarefunction:


... z = square(Array<>(x+y)); z = square<Array<> >(x+y); // OK // OK

Theotherpossibilityistopartiallyoverloadthesquarefunctionasshownbelow.Theadvantageisthatthecallsitesdonotrequireany change,butthedisadvantageisthattheoverloadinghastoknowabouttheRparameterofArray,whichotherwisenevershowsupinthe usageofthisclass.


template < typename R > Array<> square(Array<R> const& t) { return t*t; } // or, taking further advantage of the "optimization" : template < typename R > Array<A_Mul<R, R> > square(Array<R> const& t) { return t*t; }

Thereareotherusefulapplicationsofexpressiontemplates,forexampleintheBoostLambdalibrary,whichallowstocreatefunctors"onthe fly",startingfromplaceholdersvariables_1, _2...providedbythelibrary.Thiswayyoucanwritefunctorsontheflyusinganaturalsyntax like:


std::vector<double*> V; ... std::sort(V.begin(), V.end(), *_1 > *_2);

Another application can be found in the GMP library (GNU Multi Precision), which provides C++ types for computing with multi precision numbers (integers, rationals...). In this case, expression templates are used to avoid creating temporaries. We can also cite PETE (Portable Expression Template Engine) which is a tool which allows to create expression templates easily.

9. Large Scale C++ Software Design


Introduction
Thissectioncontainsmaterialfrom[Lakos96].Thisbookcontainsmanymoreexamplesthathelpinunderstandingthedifferenttechniques.

file://C:\Users\rmanoj\Desktop\Untitled.htm

1/13/2010

Page 64 of 72

We have seen various implementation aspects for single classes. We have also seen designs with several classes, for example, design patterns and template metaprograms. If we develop a large application or library, we have to consider a new level of organization: How to distribute classes and functions over files -- and also bigger units of organization. We distinguish between the logical design and the physical design. The logical design describes how to write a class or a function and how to relate them to each other. Physical design describes how to organize the code in files. In large systems, it is crucial to keep the complexity managable. One measure of complexity is the dependency between different units. Specifically cyclic dependencies increase the complexity. Complex systems are hard to understand and hard to test. Compilation times and link times can grow unmanagable large for complex systems. After some definitions, which basically set up a sane way of organizing source code into components, we see techniques how to analyse physical dependencies between packages and how to break up cyclic dependencies between packages.

Internal and External Linkage


AnameinC++hasinternal linkageifitislocaltoitstranslationunitandcannotcollidewithanidenticalnamedefinedinanothertranslation unit.Examplesaretypenamesandstaticvariables.

A name in C++ has external linkage if, in a multi-file program, that name can interact with other translation units at link time. Examples are non-static function names, member function names, and non-static global variables.

Components and Dependency Relations


Acomponentnameconsistsofoneheaderfilename.handonesourcefilename.C(orwhateversuffixforC++sourcefilesisappropriate). Componentsarenotrestrictedtoasingleclassorfunction.Theywillusuallycontainafewcloselyrelatedclassesandfunctions.Acoupleof sanityrulesapply:
name.Conlyimplementsname.handname.hisonlyimplementedbyname.C. name.Cincludesname.hfirsttoassertthatname.hincludesallheaderfilesthatareneededwhenusingname.hsomewhereelse. name.husesincludeguardstopreventmultipleinclusions.Forexample: #ifndef NAME_H #define NAME_H 1 // here goes the body of the header file #endif // NAME_H //

Somecompilersalsaccepta#pragma oncestatement.Nowadays,compilersalsodetectautomaticallyincludeguards,suchthata secondattempttoincludethisheaderfilewillnotevenresultinopeningandscanningthisfile.Thus,redundantincludeguards(those aroundincludestatementsintheincludingfileasproposedin[Lakos96])aresuperfluous. Alldefinitionswithexternallinkageinname.Caredeclaredinname.h. Wheneveranameofexternallinkageisused,weincludename.h(asopposedtojustdeclaringthenameagain). Weareinterestedinphysicaldependenciesbetweencomponents(thedependencieswithinacomponentarenotofinteresthere).Thesanity rulesmakeiteasytoseethephysicaldependenciesfromtheheaderfileinclusiongraph.

A component y DependsOn a component x if x is needed in order to compile or link y. More specifically: Component y exhibits a compile-time dependency on x if x.h is needed in order to compile y.C. Component y exhibits a link-time dependency on x if the object file y.o contains undefined symbols for which x.o may be called upon either directly or indirectly to help resolve them at link time. Compile-time dependency almost always implies link-time dependency (see also [Page 127 ff., Lakos96]). The IsA relation and the HasA relation from the logical design form always compile-time dependencies.

Physical Hierarchy
TheDependsOnrelationformsagraphovercomponents.Themajordesignruleis:Avoidcyclesinthedependencygraph!Designswithcycles arehardtounderstand.Designswithcyclescanhavemuchlargercompileandlink-timesfortesting.

Let us take a closer look on testing. We assume a test-driver program for each component. The benefit of components (and the intended modularization) is hierarchical testing. We test each compononent in isolation, before we test components that depend on this component. Of course, this does not work if we have cycles in the dependency graph. All components participating in the cycle have to be tested at once and together. However, this shows also a way out of cycles between components; reorganize the parts that participate in the cycle into one compoment (since we do not care what happens within one component). Some of the possible ways

file://C:\Users\rmanoj\Desktop\Untitled.htm

1/13/2010

Page 65 of 72

of reorganizing a design are covered in the next section. Furthermore, the link time for building all test drivers increases. We compare two worst cases for n components: First, each component depends on all other components, and second, no component depends on any other component. If we assume unit cost for each linking with one component, we get O( n2) cost for linking all test drivers in the first case, and O( n) in the second case. However, each non-trivial realistic system will have dependencies. A well designed system will aim for a flat acyclic hierarchy, approximately shaped like a balanced tree. Total linking cost would be O( n log n). The cost for linking all test-drivers can be captured in a useful metric. The Cumulative Component Dependency, CCD, is the sum over all components Ci in a subsystem of the number of components needed in order to test each Ci incrementally. Derived metrics are the avarage component dependency, ACD = CCD / n, and the normalized cumulative component dependency, NCCD, which is the CCD devided by the CCD of a perfectly balanced binary dependency tree with the same number of components. The CCD of a perfectly balanced binary dependency tree of n components is (n+1) * log2(n+1) - n. The book [Lakos96] describes tools to analyse the dependencies of components and to compute these metrics automatically, assuming the above rules for packages have been followed. The sources for the tools are available at ftp://ftp.aw.com/cp/lakos/.

Reducing Link-Time Dependencies: Levelization


Weintroduceseveraltechniquesforeliminatingcyclicdependenciesinthedependencygraph.Theunderlyingassumptionisthataninitial designisactuallylikelytobefreeofcycles,butasthedesignevolvesovertimecyclesareintroduced.

An examples: We are given a bunch of geometric objects, among others a rectangle in a component of the same name:
// rectangle.h #ifndef RECTANGLE_H #define RECTANGLE_H 1 class Rectangle { // ... public: Rectangle( int x1, int y1, int x2, int y2); // ... }; #endif // RECTANGLE_H //

Wealsoworkwithagraphicaluserinterfaceandhaveacomponentwithaclassforawindow:
// window.h #ifndef WINDOW_H #define WINDOW_H 1 class Window { // ... public: Window( int xCenter, int yCenter, int width, int height); // ... }; #endif // WINDOW_H //

Werealize,thatbothrepresent(amongothers)atwo-dimensionalboxandwewouldliketobeabletoconstructarectanglefromawindow andviceversa.Afirstattemptmightjustincludetherespectiveconstructors.Butasaconsequence,wehavetoincludetherespectiveheader filesandhaveacyclicdependency.


// rectangle.h #ifndef RECTANGLE_H #define RECTANGLE_H 1 #include "window.h" class Rectangle { // ... public: Rectangle( int x1, int y1, int x2, int y2); Rectangle( const Window& w); // ... }; #endif // RECTANGLE_H //

// window.h #ifndef WINDOW_H

file://C:\Users\rmanoj\Desktop\Untitled.htm

1/13/2010

Page 66 of 72

#define WINDOW_H 1 #include "rectangle.h" class Window { // ... public: Window( int xCenter, int yCenter, int width, int height); Window( const Rectangle& r); // ... }; #endif // WINDOW_H //

Thedependancygraphlookslikethis:

Actually, if we follow the include statements and the include guards, we will find out that the solution does not compile yet, since we include the other header file before declaring the own class. We need to use forward declarations to solve this problem. In fact, since we use the class Window only by reference in the Rectangle constructor, we do not need the full definition of the class Window, which we get by including the header file, but a declaration would be sufficient. The same is true for the class Rectangle in the Window header file.
// rectangle.h #ifndef RECTANGLE_H #define RECTANGLE_H 1 class Window; class Rectangle { // ... public: Rectangle( int x1, int y1, int x2, int y2); Rectangle( const Window& w); // ... }; #endif // RECTANGLE_H //

// window.h #ifndef WINDOW_H #define WINDOW_H 1 class Rectangle; class Window { // ... public: Window( int xCenter, int yCenter, int width, int height); Window( const Rectangle& r); // ... }; #endif // WINDOW_H //

However,inordertoimplementtheconstructorsinthesourcefilesrectangle.Candwindow.Ctherespectivelyotherheaderfilehastobe includedagain.Theresultingdependencygraphshowsthatwestillhavethecyclicdependencybetweenthecomponents.Wejusthave reducedthecompile-timedependencies,notthelink-timedependencies.

Definition: A subsystem is levelizable if it compiles and the graph implied by the include directives of the individual components (including the .C files) is acyclic. Thus, our example so far is not levelizable. We will see now some techniques to break cycles and to make a design levelizable.

Escalation

file://C:\Users\rmanoj\Desktop\Untitled.htm

1/13/2010

Page 67 of 72

Escalationbreaksacyclebyliftingtheinterdependentfunctionalityonelevelupintoanewcomponent.Theinterdependentfunctionalityis supposedtobesmallcomparedtotheinvolvedcomponents.Thus,theextractedfunctionalityissmallenoughtobeputinasinglecomponent. Inourexampleweintroducethecomponent,boxutil,thatcontainsonlythetwoconversionfunctions,hereasstaticmemberfunctionsofa class.Therectangleandthewindowcomponentremainuntouched.


// boxutil.h #ifndef BOXUTIL_H #define BOXUTIL_H 1 class Rectangle; class Window; struct Boxutil { static Window toWindow( const Rectangle& r); static Rectangle toWindow( const Window& w); }; #endif // BOXUTIL_H //

Demotion
Demotionissimilartoescalation.Butinsteadofcollectingtheinterdependentfunctionalityinacomponentinalevelup,wecollectthe functionalityoneleveldown.Itdoesnotworknicelywithourrunningexample.

Factoring
Factoringisthegeneralversionofescalationanddemotion.Theinterdependentfunctionalityisisolatedandrepackagedincomponents,not necessarilyinasinglecomponent.However,thegoalistoreducethecomplexityoftheremainingcycle.

Opaque Pointers
Definition:AfunctionfusesatypeTin sizeifcompilingthebodyoffrequireshavingfirstseenthedefinitionofT.

Definition: A function f uses a type T in name only if compiling f and any of the components on which f may depend does not require having first seen the definition of T. Examples for in name only are reference and pointer types. Both definitions extend naturally for components. Components that use objects in name only can be thoroughly tested, independently of the named object. Examples are container classes, nodes, and handles that just pass their data as pointers around.

Redundancy
Thisisnotnecessarilyatechniquetobreakcycles,buttoreducecouplinganddependenciesingeneral.Theideais,wheneveronlyasmall fractionofacomponentisactuallyusedinanothercomponentandcausesthedependency,itmightbeworthwhiletoreimplementthissmall fractionagainintheothercomponent.Consideranexampleofacellclass,thatcontainsamongothersaname.Thenamehasbeen implementedasastring,butisstillpresentedattheinterfaceofcellasanoldC-stylechar*pointer.
// cell.h #ifndef CELL_H #define CELL_H 1 #include class Cell { std::string d_name; // ... public: Cell( const char* name); const char* name() const; // ... }; #endif // CELL_H //

Hereitmightbeworthwhiletoreimplementthesmallfractionweneedfromstring,namelystoringadynamicallyallocatedarrayofcharacters.
// cell.h #ifndef CELL_H #define CELL_H 1 class Cell { char* d_name; // ... public: Cell( const char* name); Cell( const Cell& name); ~Cell(); Cell& operator=( const Cell& cell); const char* name() const; // ... };

file://C:\Users\rmanoj\Desktop\Untitled.htm

1/13/2010

Page 68 of 72

#endif // CELL_H //

However,itisarguableforthisexamplethatadependencyonstringsisnotabigissue,andthattheoldC-styleinterfaceisactuallyabitclunky.

Callback
Callbackfunctionsallowtobreakacycle.Typicalexamplearegraphicaluserinterfaces,orthesimpleqsortfunctioninthestandardClibrary:
NAME qsort - sorts an array SYNOPSIS #include void qsort(void *base, size_t nmemb, size_t size, int (*compar)(const void *, const void *)) Thefunctionpointercomparisthecallbackfunction.However,callbackfunctionsaredifficulttounderstand,debug,andmaintain.

Reducing Compile-Time Dependencies: Insulation


Wegivealistofpartsinaclassthatcancreatecompile-timedependencies.Acompile-timedependencyexistforanothercomponentusing thiscomponentiftheothercomponenthastoberecompiledifoneofthefollowingpartsinthiscomponentchanges: Baseclass(incl.privateinheritance). Layering(HasArelationship,butnotHoldsArelationship,whichisbynameonly). Inlinefunctionsandinlinememberfunctions. Privateandprotectedmembers. Compilergeneratedmemberfunctions,suchastheassignmentoperator. Includedirectives. Defaultarguments. Enumerations. Insulationtechniqueseliminatetheabovedependencies,forexample,compilergeneratedmemberfunctionscanbeexplicitlyimplemented, eveniftheyperformthesameasthedefaultimplementationwould.Incasethatafuturerevisionwouldliketochangethissemanticsitcould beimplementedwithoutrecompilingdependentcomponents.

Besides the obvious ones, we address two techniques for partial insulation in the next section. Two techniques for full insulation are covered in the section thereafter. Sometimes, going from partial insulation to full insulation is very easy. But sometimes, the last 5 percent are the hardest and the most costly. Full insulation is usually not appropriate at the bottom layers of a library. Full insulation is appropriate at the higher layers of a library that are exposed to the users. Major runtime costs for insulation can happen because inline functions are no longer possible, virtual dispatch tables can add another indirection, and dynamic allocation of memory is slow. Memory use can increase with dynamic memory or virtual tables.

Techniques for Partial Insulation


AHasArelationshipcanbechangedtoaHoldsArelationship.Thecostforthisisusuallydynamicmemorymanagement.

A private member function can be changed to a static non-local function of the component. If the private member function can be implemented using the public interface of the class, we just need to add the this pointer as an explicit function argument. If the member function needs exclusive access to private member variables, references to the private member variables can be added to the function signature. The price could be a penalty in runtime: the extended function signatures with larger parameter list cost time when calling the function if the function is not inline.

Techniques for Full Insulation


Definition:Anabstractclassisaprotocol classif 1. itneithercontainsnorinheritsfromclassesthatcontainmemberdata,non-virtualfunctions,orprivate(orprotected)membersofany kind, 2. ithasanon-inlinevirtualdestructordefinedwithanemptyimplementation,and 3. allmemberfunctionsotherthanthedestructorincludinginheritedfunctions,aredeclaredpurevirtualandleftundefined. Aprotocolclassisanearlyperfectinsulator.ProtocolclassesinC++aresimilartointerfacesinJava.Severalofthedesignpatternshave protocolclassesaspartoftheirdesign,forexampletheadaptorpattern.

Another technique for full insulation is an opaque pointer. The insulated class contains only one opaque pointer to its private data and no other member variables.
// Insulated.h #ifndef INSULATED_H #ifndef INSULATED_H

file://C:\Users\rmanoj\Desktop\Untitled.htm

1/13/2010

Page 69 of 72

class Insulated_private; class Insulated { Insulated_private* d_data; // opaque pointer public: // ... constructors and member functions }; #endif // INSULATED_H //

The.Cfileimplementstheprivatedatatypeandallmemberfunctionsandconstructors.The.Cfilecanbechangedandrecompiledwithout forcinganyothercomponenttorecompile.
// Insulated.C #include "Insulated.h" class Insulated_private { // ... }; // ....

10. Overview of Some Foundation Libraries


Introduction
ThefollowingoverviewhighlightsonlyafewaspectsofthedifferentC++librariesthatareofinterestforthiscourse.Alllibrariesimplement strings,lists,mapsandsimilarcontainerclasses.Allemphasizetheseparationofpolicyandimplementation.

Gnu C++ Library


Thelibg++wasoneofthefirstavailableC++libraries.Itbecameavailablefirstin1985.Itcontainsstrings,numbertypes,containerclasses,IO streams,storageallocators,andC-libwrappers.See[page33ff,Lippman96].

An emphasized distinction was between the view on abstract data types with value semantics and object-oriented implementations with state changing operations. Smaller classes, such as complex numbers and strings, are implemented with value semantics, while the container classes are implemented in the object-oriented way. An implication of the value semantics for strings (and others) is that modifying operations are most naturally implemented such that they return a newly created object. See for example an operator + to concatenate two strings:
class string; string operator+ ( const string& s1, const string& s2);

Thereturntypecausesproblemsifwederiveaclassfromstringandusethisoperator.
class special_string : public string { ... }; void foo() { special_string s1, s2; special_string s3 = s1 + s2;

// type error

Wegetatypeerrorbecausewecannotassignastringtoaspecial_string.Theclassstringisnotsuitableforsubclassing.Tolessenthe temptationtoderivefromstring,thedevelopersoflibg++gavethestringclassarich(nottosayfat)interface.

The string class was reference counted at the beginning using copy-on-write for modifying accesses. Reference counting has been eliminated later with the argument that reference counting is too clever and the user knows better where to copy and where to use references. (An interesting yet exceptional opinion. I have not seen this opinion anywhere else yet. However, we had the discussion in CGAL with reference counting for the small kernel objects. Here, it pays off once we use number types that are bigger than the builtin number types.)

Tools.h++
Tools.h++evolvedoutofinhousedevelopmentsatRogueWave1987.Ithasabout40000linesofcode.Asisterlibraryfromtheseinhouse developmentsisMath.h++.ToddVeldhuizen(authorofBlitz++)wasaninternatRogueWave,whichmotivatedhisworkonefficientvector expressionsinBlitz++.See[page43ff,Lippman96].

Tools.h++ provides container classes following the abstractions of the Smalltalk class hierarchy. However, the classes in Tools.h++ are no derived from a common base class. They form a loose set of classes. Each container class is provided in three forms; as a class template, as a generic implementation based on the C-preprocessor (based on the genric.h facilities), and as a heterogeneous collection (Smalltalk like). The template-based collections are availbale in three versions; intrusive, value-based and pointer-based collections. Consider the

file://C:\Users\rmanoj\Desktop\Untitled.htm

1/13/2010

Page 70 of 72

example of a list. An intrusive list assumes that the items have already the pointers needed to link them into the list. The value-based list copies the items into internal nodes, just as the STL container classes do. The pointer-based list keeps only a pointer to the items. Value- and pointer-based lists can handle arbitrary item types. Strings are reference counted using copy-on-write for modifying functions.

Booch Components
TheBoochcomponentsstartedasanADAlibrary,1984-1987.Theyconsistedof501packageswithabout150000linesofcode,whichisabout 125000linesofnon-commentsourcelines(NCSL).ThefirstC++releasein1990wasalreadyshortenedto17000NCSLandcontinuedtoshrink to15000NCSLwithrelease1.4.7andto15000NCSLwithrelease2.0.Thiscodesizereductionwasevenaccompaniedbyanincreaseof flexibilityandfunctionalityofthelibrary.However,futurereleasesareexpectedtoaddmorefunctionalitythatwillfinallyincreasetheNCSL countagain.See[page59ff,Lippman96].

The library contains monolithic data structures, which are stacks, strings, queues, deques, rings, maps, sets, bags, and polylithic data structures, which are lists, trees and graphs. When copied, a monolithic data structures perform a deep-copy (copy all of its contained items). Polylithic data structures can share substructures, which are sublists, subtrees, or graph nodes and edges. When copied, a polylithic data structures performs a shallow copy (it just creates pointers to the original parts). Each container class can have several different implementations. The implementation can be bounded or unbounded, and the implementation can provide different forms of synchronisation for multi-threaded program executions. The library distinguishes between the abstract data types (the interfaces) and the concrete data types (the implementation). The set of available implementations have been rigorously factored out and reduced to three different implementations: Simple_List allocating its nodes on the heap, Simple_Bounded_List allocating a limited number of nodes from the stack, and Simple_Vector allocating its array from the stack. This rigoruous factoring reduced the library size coming from Ada by 20-30%. Polymorphic iterators increased the flexibility and functionality of the C++ library and reduced its size by about 50%. Virtual constructors addresses memory allocation schemes. Factoring memory allocation and its synchronisation into its own components saved another 50%. Multi-threaded support is factored into a lock-class. Resource allocation and de-allocation is handled in the constructor and the destructor respectively. Moving to lock-classes saved another 50%. So this report doesn't seem to be a recommendation for Ada.

LEDA, the Library of Efficient Datatypes and Algorithms


LEDAstarted1988attheMax-PlanckInstituteinSaarbrcken,Germany.Intherelease4.0itconsistsof210000linesofcode.However,this countincludesthecomments,whichcontainalsothereferencemanualofLEDA.[Mehlhorn99]. Datatypes Numbertypes Graphsandsupportingdatastructures(nodearrays,nodepriorityqueues,...) Graphalgorithms Grapheditor Geometricobjects Geometricalgorithms 2d-and3d-visualization LEDAaimsforthe(theoreticalandpractical)fastestsolution,andusuallyprovidesseveralimplementationsofthesamedatastructure,for example,fordictionariesorpriorityqueues.LEDAcoversalgorithmsanddatastructureofmosttextbooks.

An important design goal for LEDA is ease-of-use. Programs in LEDA look similar to their pseudo-code counterparts in text books. Therefore, LEDA claims to fulfill the equation "algorithm + LEDA = program", or emphasizing LEDA's performance and often worst-case optimality "algorithm + LEDA = efficient program". LEDA uses a concept of items that is similar to iterators. A main difference to iterators is that the access of another item from the current item (e.g. going to the successor item in a list) requires the container class. For the ease-of-use LEDA defines convenient macros for for-loops, here some examples:
forall_items( item, container) forall( elems, container) forall_nodes( node, graph) forall_edges( edge, graph) forall_adj_edges( edge, node) { { { { { ... ... ... ... ... } } } } }

file://C:\Users\rmanoj\Desktop\Untitled.htm

1/13/2010

Page 71 of 72

Ease-of-useandflexibilitycancomewithapricetagontheperformanceofthelibrary.ForLEDA,theoverheadinmemorysizeisclaimedtobe lessthantwoandinruntimelessthanthreecomparedtohand-codedsolutions.

The designers of LEDA did not choose to implement data structures as templates for the following reasons:
1. Longcompilationtimesfortemplates. 2. Sourcecodemustbeexposedtolibraryclient. 3. TemplatesnotavailablewhenLEDAstarted. ThecurrentdesigninLEDA(version3.0and4.0)splitsdatastructuresintoabaseclassandaderivedclasstemplate.Thebaseclassimplements asmuchfunctionalityaspossiblebasedonvoid*pointersfortheuserdata.Itcanbecompiledanddistributedinobject-codeformat.The derivedclasstemplatebuildsawrapperaroundthebaseclassthatdoesthesafetypecastsbetweenvoid*pointersandtheuserdata.The derivationisactuallyaprivatederivationandwouldn'tbenecessaryforthewrapperfunctionality.

Instead, this derivation has been chosen as a compact solution to provide the base class with some knowledge how to operate on the user data, for example, how to copy and how to compare it, using dynamic binding at runtime. For this, the base class defines abstract virtual functions accepting void* pointers, and the derived class template performs the required operation after casting the void* pointers back to the user data. Here is a small example for an ordered pair as a data structure. The comparison function is implemented using a virtual member function. First, the base class that can be compiled and put into a library. For the simplicity of presentation here it is implemented inline. Note that the void* pointer storage requires dynamic allocation. We ignore the proper copy and destruction functionality here. (see also leda_impl.C)
class pair_impl { void* d_min; void* d_max; protected: // this class is only ment for derivation // to be implemented in derived class: virtual bool cmp( void* a, void* b) = 0; // note, constructor doesn't work here instead of set() because // the virtual cmp function wouldn't be available (will be available // after derived class is constructed). void set( void* a, void* b) { if ( cmp(a,b)) { d_min = a; d_max = b; } else { d_min = b; d_max = a; } } void* min() { return d_min; } void* max() { return d_max; } };

Second,the(privately)derivedclasstemplate.Itimplementsthecomparisonrequiredbythebaseclass.
template <class T> class pair : private pair_impl { virtual bool cmp( void* a, void* b) { // use casts to T return * static_cast<T*>(a) < * static_cast<T*>(b); } public: pair( const T& a, const T& b) { set( new T(a), new T(b)); } T& min() { return * static_cast<T*>( pair_impl::min()); } T& max() { return * static_cast<T*>( pair_impl::max()); } };

Thisseparationintoderivedclasstemplatesandbaseclassescoincidewiththeseparationintodata types,suchasadictionary,andpossible implementations,suchasbalancedtrees,skiplists,etc.Thisseparationhasalsobeenmentionedaspolicyversusimplementationfortheother libraries.

The default dictionary array in LEDA is called d_array<I,E>, where I is the index type and E is the associated element type. Since LEDA does not rely on template default arguments, a different name has been given to the dictionary type where the user can select from different implementations, _d_array<I,E,Impl>. The dictionary with implementation parameter is (publicly) derived from the default dictionary and its member functions are virtual. Thus, a use can write generic algorithms based on the default dictionary, and still choose a specific implementation when using it. Both dictionaries are implemented using the private inheritance from the implementation. Using above example with the ordered pair data structure, we get the following inheritance relationship:
pair_impl <-----pair<T> ^ | | Impl <-----pair_x<T,Impl>

file://C:\Users\rmanoj\Desktop\Untitled.htm

1/13/2010

Page 72 of 72

SinceImplcouldbeequaltopair_impl,wehavetomaketheprivateinheritancevirtual.Assumingwealsohadmadetheminandmax memberfunctionsofourpairclassfromabovevirtual,wecancontinuewiththederivedclassincludinganimplementationparameter.(see alsoleda_impl2.Cforthefullexample)


template <class T, class Impl> class pair_x : private virtual Impl, public pair<T> { virtual bool cmp( void* a, void* b) { // use casts to T return * static_cast<T*>(a) < * static_cast<T*>(b); } public: pair_x( const T& a, const T& b) { Impl::set( new T(a), new T(b)); } virtual T& min() { return * static_cast<T*>( Impl::min()); } virtual T& max() { return * static_cast<T*>( Impl::max()); } };

Notethatthissolutionderivesactuallyfromtwoimplementations,thuscarryinganunusedimplementationanditsspacearound.

There are two major sources of inefficiencies in this design: the dynamic memory allocation and virtual function calls for basic operations, such as comparisons. LEDA partially compensates these inefficiencies with two optimizations. The first optimization gets rid of dynamic memory management for small data types. LEDA distinguishes between big data types and small data types. Small data types are small enough to fit into the size of a pointer. LEDA uses a smart pointer, called GenPtr, to actually store small data types in the place of the pointer instead of allocating memory. Only for big data types, LEDA uses dynamic memory, which is then drawn from an efficiently self-managed pool of memory. The second optimization selectively optimizes operations for builtin data types to get rid of the overhead of calling a virtual function for rather basic tasks. An example is the binary search in ordered dictionaries, which is handcoded for integers to call the operator < directly. However, this path for optimizing LEDA's performance is not readily available in the documentation for the library users. Though it could be done already by deriving from a container class and rewriting the member functions in question. In conclusion, when comparing with LEDA, choose a builtin type that fits into a pointer (e.g. int) to benefit from LEDAs optimizations, or write a trivial class slightly larger than a pointer, to see the full overhead of LEDAs design.

file://C:\Users\rmanoj\Desktop\Untitled.htm

1/13/2010

You might also like