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:
General design methods: here Generative Programming and Domain Analysis with Feature Diagrams   Studying the expressiveness of the library implementation language C++   Studying example libraries   Studying individual and often occurring problems  

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: determine the scope of the domain and define a set of reusable, configurable requirements for the systems in the  domain.   Domain design: develop a systems architecture and a production plan.   Domain implementation: implement the reusable assets, from components up to the configuration knowledge.  

In more detail from the domain engineering method DEMRAL [Czarnecki00]:
1. Domain analysis  1. Domain definition  1. Goal and stakeholder analysis   2. Domain scoping and context analysis  1. Analysis of application areas and existing systems   2. Identification of domain features   3. Identification of relationships to other domains   2. Domain modeling  1. Identification of key concepts   2. Feature modeling of the key concepts, i.e., identification of commonalities, variabilities, feature dependencies, and feature  interactions   2. Domain design 

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

1/13/2010

Page 2 of 72

1. Identification and specification of the overall implementation architecture   2. Identification and specification of domain specific languages   3. Specification of the configuration knowledge   3. Domain implementation  

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: all features  Optional features: any feature can  have to be present.  be chosen optionally. 

    Alternative features: exactly one  Or-features: at least one feature  has to be chosen.  feature has to be chosen. 

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: If one feature in an alternive is  Optional or-features: If one feature in an or-feature is optional then the `at optional then all features in that alternative are optional.  least one feature' requirement becomes meaningless and we normalize to a  set of optional features. 

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: A description for each feature. Informal text or any other suitable formalism can be used here.   Rational: Why is this feature in this model? Conditions and recommendations when and when not to select a variable feature.   Stakeholders and client programs: A list of stakeholders (user, customers, managers, etc.) who are interested in this feature, and in the  case of a component a list of client programs that need this feature. (Such a list can be helpful to avoid unnecessary feature bloat.)   Exemplar systems: Is possible, mention known systems that have this feature.   Constraints and default dependency rules: Dependency rules can be expressed between any set of variable features. We have mutualexclusion constraints and require constraints. Default dependency rules help in automatically configuring a system.   Availability sites, binding sites, and binding mode: Availablity sites describes when, where, and to whom a variable feature is available,  and binding site describes when, where, and by whom a feature may be bound. Binding mode determines whether a feature is  statically, changeably, or dynamically bound.   Open/closed attribute: A feature is open if we expect more variable direct subfeatures, for example, the number types possible for an  element in a matrix class might increase in the future. Otherwise we would mark the feature as closed.   Priorities: Records the relevance for the project.   Implementation strategies: Identifying implementation strategies for this feature (already part of the domain design).  

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++ function overloading, templates, C preprocessor -DNDEBUG   Link time: makefile configuration   Load time: dynamic link libraries   Infrequent at run time: Sun's HotSpot technology for Java   Frequent at run time: variable values, virtual member functions  

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: Named properties of an ADT, e.g., size of a container. Features of an attribute can be; mutable versus const, volatile or not,  stored or computed, internally or externally owned.   Data structures: Used to implement the ADT on top.   Operations: Accessors and the core algebra operations. Signature, i.e., name, operands, operand types. Different alternative  implementations. Possible optimizations and specializations. Binding mode and binding time.   Error detection, response, and handling: Pre- and post-conditions, invariants. Exception safety.   Memory management: Heap or stack. Custom or standard allocation. Thread safety. Persistence.   Synchronization: Thread safety of shared data.   Persistency: Storing and restoring a state of the ADT to and from disk.   Perspectives and subjectivity: Different views of stakeholders or different client programs on the ADT.  

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

1/13/2010

 Possible classification of  several algorithms into groups.. Stupid) principle that can be used to fend of "creeping featurism". iterators or data accessors.g.   Odds and Ends From software engineering we list some general design goals one should keep in mind: Flexibility  Adaptability (to existing environment)   Extensibility   Openness (to other standards/libraries)   Ease of Use  Smooth learning curve (number of concepts. constructors and destructor.de). virtual member  functions. and sqrt( sqrt( 1000000)) = 31.   Structure: The structure of the representation.   Parallelization: Related to optimization.Page 4 of 72 For container like ADTs we can also consider: Element type: The types managed by the ADT.   2. structure)   Uniformity   Complete and minimal interfaces   Rich and complete functionality (is it useful?)   Efficiency  Space and time   O-calculus   Practice (log(1000000) = 7.   The feature starter set for algorithms: Computational aspect: The functional description of the algorithm."   The words are those of the medieval English philosopher and Franciscan monk William of Ockham (ca.   file://C:\Users\rmanoj\Desktop\Untitled.g. response. e. derivation. e.mpg. We repeat shortly classes. Last modified on Tuesday. 1285-1349). text book description or pseudo code.   Memory management: Might be needed here as well for local data or optimization purposes.6)   Worst case/average case   Input model.   Indexing: Are the elements accessible with an index.g. e. are the hard cases realistic?   Correctness  Documentation   Hard because of complexity   Robustness  Rounding errors (in geometric)   Boundary cases.g. and static class members. We continue then with an introduction to templates. matrix. It leads to the useful KISS (Keep It Simple. size=2 width="100%" align=center>   Lutz Kettner (<surname>@mpi-sb. here for specific hardware or system architectures. 17-Jan-2006 17:53:41 MET. The C++ Language Introduction A basic familiarity with C++ is assumed.-).. with integer keys or symbolic keys.   Error detection. [Lippman98] (more basic). degenerate situations (in geometry)   Modularity   Maintainability   .   The famous Occam's razor: "Pluralitas non est ponenda sine neccesitate" or "plurality should not be posited without necessity. searching and sorting. Recommended introductions for C++ are  [Stroustrup97] (advanced). e.htm 1/13/2010 . e..   Optimizations.g. and handling... [ISO-C++-98] (the actual standard .. member functions..   Data access: The interface to the ADTs.

}. Constructors. but larger code.get_i(). which is also known as an instantiation of  class A. // private public: int get_i() { return i. }. the inline declaration is automatically assumed. } }. If we create a variable of type A. class A { int i. // private public: int get_i(). int j = a. Public members can be used from everywhere. int main() { A a. Member functions can just access all other class members. It is usually placed in a header file *. } The member variable i is now inaccessible from the outside and the object can only be manipulated through its member functions. Otherwise.  A member function looks like a normal function declaration within a class definition.h. To check const correctness across classes and member functions.set_i(5).htm 1/13/2010 . } // inline and const void set_i( int n) { i = n.Page 5 of 72 Classes and Member Functions struct A { int i.   int A::get_i() { return i. Thus above definition of A is equivalent to:   class A { public: int i. // a2. Variables can be declared const in C++. Each object of type A has a member variable i which can be accessed with the dot notation:   int main() { A a.  Inline functions have to be defined before their use. functions and member functions (collectively called functions in the following) can be declared inline. } // both inline void set_i( int n) { i = n. We call the type A a class. a. a member function has to be  declared const if it does not change the member variable of the class.set_i(5). Private members can only be used  within the class. } void A::set_i( int n) { i = n. }. Assignment. It  is usually placed in a source file *. void set_i( int n). Protected members can also be used by derived classes. } // uninitialized constant // this is forbidden by C++ type system // So. // private public: int get_i() const { return i. A member functions definition is written outside of the class definition and uses the scope operator :: to name the function within the class. instead of creating a function call and a separately compiled  function body. Not so small inline functions can still lead to faster. const A a2.C. whichever default is more convenient. a member function cannot be called for an object that is  declared const. const A is currently pretty useless. Small inline functions can lead to faster and smaller code. a.)   class A { int i.i = 5. or public.get_i(). we call this variable an object of class A. thus their implementation moves usually to the header file. } For efficiency. and Destructor Constructors are special `member functions' of which exactly one is called automatically at creation time of an object. The members in  struct are by default public. Which one depends on  file://C:\Users\rmanoj\Desktop\Untitled. a. If the member function is  defined within the class. This hidden function parameter is named this and is of type A*. } C++ provides access control for class members.set_i(5).   class A { int i. the C++ compiler adds  automatically a hidden function parameter that points to the current object. So the complete definition of our example class A looks as follows: (See also const correctness. the members in class are by default private. int j = a. which can be either of private. } // inline }. To accomplish this. which advises the  compiler to replace a function call directly with the function body if possible. protected.   int main() { A a. We will use struct or class interchangeable.

buf. and all others (user defined). // // // // default constructor copy constructor.g. dynamically allocated memory. } Buffer( const Buffer& buf) : p( new char[100]) { memcpy( p. A new syntax. there exists a destructor which is automatically called at destruction time of an object. Note that the compiler would  create a default implementation for the assignment operator if it is not defined explicitly. Note the use of this. // calls the default constructor A a2 = a1. The default destructor does nothing. Buffer newbuf( buf). } }. } return *this. equal to the compiler default user defined destructor. The order of the initializations should follow the order of the declarations.p = tmp. which resembles constructor calls. // private public: A(). The parameter list for the default and the copy constructor are fixed. allows to call the respective constructors of the member variables (instead of assigning new values). // exchange it with 'this' // the destructor of newbuf cleans up the data previously // stored in 'this'. or destructor automatically. equal to the compiler default Usually.. only the default constructor (if the semantics are reasonable). // }.i) {} A( int n) : i(n) {} ~A() {} }. copy constructor. // construtor to allocate n bytes for buffer file://C:\Users\rmanoj\Desktop\Untitled. The constructor call syntax extends also to built-in types. // calls the user defined constructor } // automatic destructor calls for a4. (see also Buffer. a2. public: Buffer() : p( new char[100]) {} ~Buffer() { delete[] p. 100). } Buffer& operator=( const Buffer& buf) { // Check for self-assignment. buf. } void swap( Buffer& buf) { char* tmp = p. copy constructor. a1 at the end of the block The compiler generates a missing default constructor. but its only an optimization if ( this != & buf) { // In general: perform copy constructor and destructor // Make sure that self-assignment is not harmful.C)   class Buffer { char* p. Automatic Conversion and the explicit Keyword for Constructors A user-defined constructor with a single argument defines a conversion between two types. e. the following four implementations have to work together to  avoid resource allocation errors: default constructor. All  default implementations can be explicitly inhibited by declaring the respective constructor/destructor in the private part of the class. and destructor. Symmetrically. the pointer to the current object. // calls the copy constructor (not the assignment operator) A a3(a1).   struct Buffer { Buffer( int n).p. See the following example and [Item 11 and 17. The user defined constructors can have arbitrary parameter lists following C++ overloading rules.  Three types of constructors are distinguished: default constructor. a3.  Constructors initialize member variables.p. class A { int i. p = buf. // A( int n). assignment operator. // A( const A& a). The default implementation of the default  constructor calls the default constructors of all class member variables.htm 1/13/2010 . // private public: A() : i(0) {} A( const A& a) : i(a. The default constructor is not automatically generated if other  constructors are explicitly declared in the class (except an explicit declared copy constructor).Page 6 of 72 the form of the variable declaration. As soon as  the class manages some external resources. the type of the argument and the class the  constructor belongs to. // create the new copy swap( newbuf). and some user defined constructors are defined for a class. copy constructor. The default copy constructor calls the copy  constructor of all class member variables. multiple initializations are separated by comma. default constructor copy constructor user defined destructor int main() { A a1. // ~A().  Meyers97]. A constructor has the same name as the class and it has no return type. which performs a bitwise copy for built-in data types. class A { int i. The C++ compiler is allowed to perform this conversion automatically to find a matching function call. // calls the copy constructor (usual constructor call syntax) A a4(1).

 A base class defines an  interface with virtual member functions. the compiler choses the declaration. // function declaration S w = S( int(d)). We solve this in the following example and use default  values to implement the three constructors in one. }. public: B( int n = 0.   class B : public A { int j. B inherits all member variables and all member functions from A. Virtual Member Functions Virtual member functions and derivation provide the backbone of flexibility in C++ for the object-oriented paradigm.. j(m) {} }. It is advised to forbid them with the new keyword explicit. // oops. but it can access only those that are  not private. int m = 0) : A(n). }. void foo( double d) { S v( int(d)). file://C:\Users\rmanoj\Desktop\Untitled. so S v( int d).   struct Shape { virtual void draw() = 0.Page 7 of 72 // . // object declaration S x(( int(d))). } Constructors and destructor are not inherited.. here a pure virtual member function. // a function to rotate buffer cyclically int main() { rotate( 5). Objects of class B can be assigned to objects of class A.C) Derivation If we derive a class B from a base class A. void rotate( Buffer& buf). is obviously a function declaration and not an  object of type S that gets initialized with d casted to int. they loose their additional member  variable j.htm 1/13/2010 . In case of ambiguities between a statement and a declaration. Doing so..   int main() { B b. }. either by using the other intializer notation as for w or by adding parentheses that exclude the function declaration as for x.  To get the second interpretation we have to disambiguate the declaration explicitly. public: B( int n) : A(n) {} B( int n. j(m) {} }. // . a temporary Buffer initialized with 5 will be created } These automatic conversions can be a source of errors that are difficult to spot. We consider only public inheritance here (inheritance can also be qualified as private)..   struct Circle : public Shape { void draw(). We derive different concrete classes from Shape and implement the member function draw for each of them. Calling a base class  constructor explicitly follows the same syntax as the member variable initialization. user defined constructors are missing and must be repeated. Ambiguity between Function-Style Cast and Declaration The constructor notation and its implied function style cast gives rise to a couple of ambiguities in C++ and the heavily overloaded use of  parentheses. B has now two integer variable member.   class B : public A { int j. Only the additional. }. (see also decl. }. The first constructor and the default constructor leave the value of j uninitialized. A a = b.   struct S { S(int). // object declaration } v is a function declaration because the parentheses around d are redundant. int m) : A(n). But the default implementations of the derived class call automatically the respective  implementations of the base class.   class B : public A { int j.   struct Buffer { explicit Buffer( int n).

C source file. Meyers97] for how to get around this restriction with local static variables in global functions). It can only access static member variables. It is called like a normal function using the scope operator. A static member function omits this pointer. (see also Shape. Each object gets an additional  pointer referring to this table.  Since we don't know the size of the actually allocated objects any more. int Counter::counter = 0. but it has a pointer (this) to the current object hidden in its parameter list. // calls Square::draw delete s1. Shape* s2 = new Square. s1->draw(). Counter b. additional pointer and one more indirection for a function call. int j..   int main() { Shape* s1 = new Circle. struct Square : public Shape { void draw(). but the order is unspecified between different compilation units. A member function exists already only once per class. It is sufficient to define a virtual. int main() { Counter a. (dispatch table with function pointers). s1->draw()..Page 8 of 72 }.C)   #include <iostream.h> struct Counter { static int counter. Counter c. }. Circle::draw or Square::draw. Shape* s2 = new Square. } }. They can be accessed from outside the class with the scope operator. virtual ~Shape() {} }. we also have to use a virtual member function to delete the objects properly. This definition is supposed to show up in only one  compilation unit (much like a global variable). delete s2. We cannot create an object of a class that contains pure virtual member functions. Thus. // calls Circle::draw s2->draw(). }.htm 1/13/2010 . } Note that an explicit definition of the static member outside of the class is needed. a class that counts how many objects of its type have been created looks as follows: (see also Counter. // calls Circle::draw s2->draw(). // calls Square::draw } This runtime flexibility is achieved with a virtual function table per class. see Automatic Library Initialization and Housekeeping. These extra costs. 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. empty destructor in the base class. For  example. the derived shape classes int main() { Shape* s1 = new Circle.C) struct Shape { virtual void draw() = 0. not to the object. The order of initialization for multiple static member variables is specified to be in the order of declaration in a single compilation unit.  Static member variables are guaranteed to be initialized before main gets executed. the definition is usually in a *. the program figures out at runtime which member  function is meant. struct A { static int i. but we can have pointer of this type and we can assign  pointer of the derived types to them. each object knows at runtime of which type it is. A static variable can be used to initialize a library automatically. static void init(). If we call a member function through this pointer. cout << Counter::counter << endl. } Static Class Members Static member variables belong to the class. // . are only imposed on objects which class or base classes  use virtual member functions. file://C:\Users\rmanoj\Desktop\Untitled. which is also used for the runtime type information in  C++. Counter() { counter++. Thus. A static member function cannot access member variables of the object.

}. Features like default arguments and specialization (see below) work also on this kind of parameter. class Container = list<T> > struct stack { . } The C++ compiler uses pattern matching to derive automatically the template argument types for functions template. The parameter is determined at compile time and thus constant. An example is a swap function that exchanges the value of two variables of arbitrary types.htm 1/13/2010 . Note the space between the two > >. and the name of  the list for the scope. (in particular. class templates can also have builtin integral types as template parameters. e. template <class T.Page 9 of 72 void A::init() { i = 5. Template arguments can have default values. }. Otherwise this would be parsed as the right-shift operator. swap( i. }.  C++ supports two kinds of templates: class templates and function templates. ls..   template <class T> struct list { void push_back( const T& t). which is list<T>. list is a class template. b = tmp. assert( A::i == 5). template <class T> void swap( T& a. Besides type parameters. Consider for example a function template that works only for lists: template <class T> void foo( list<T>& ls). a stack class built with our list. and list<T> is a class). We can use it to declare a fixed size array of coordinates for the point.. the template arguments must be constant expression for that integral type. } Templates Templates provide the backbone of flexibility in C++ for the generic-programming paradigm. Therefore. In that case. This process is called template instantiation.   template <class T> void list<T>::push_back( const T& t) { . while list<T> is a template class.. all template arguments must be used somewhere in the function parameter list. For the naming convention.. A useful example are points of constant dimension. // coordinate array // . The actual types for a function template are implicitly given by the types of the function arguments at instantiation time. int j = 7. a = b. // append t to list. The actual types for a class template are explicitly provided by the programmer. template <int dim> struct Point { double coordinates[dim]. Their flexibility is resolved at compile time thus  retaining efficiency.. int main() { list<int> ls. // is not allowed } int main() { A::init().. } // uses "int" for T. } int main() { int i = 5.. T& b) { T tmp = a. int main() { Point<3> // a point in 3d space } file://C:\Users\rmanoj\Desktop\Untitled. // fine // j = 6. The compiler generates a separate translation of the component with actual types replacing the formal placeholders wherever this template is used. Templates are incompletely specified components in which a few types are left open and represented by formal placeholders. the template arguments. // uses "int" for T.  list is a template. An example is a generic list class for arbitrary item types. j). } Defining push_back outside of the list class requires the repetition of the template declaration template <class T>.push_back(5).g.

Now. Here is. pair() {} // don't forget the default constructor if there are also others // template constructor template <class U1. function  templates were easy to extend to member functions and even constructors (although late in the standardization process).C)   template <class T1. if we could create one pair from another pair.first). let's assume we want to define this constructor not inline (however. In the chapter on the STL we will see examples where this useful to implement generic adapters for iterators. this causes no problem in C++. Quicksort is not efficient on lists (needs an extra container).   The small convenience class pair<T1.Page 10 of 72 The C++ standard aims for separate compilation of templates. we can use the list class with a type that is not comparable. "Lazy" Implicit Instantiation As already mentioned above. a more efficient merge sort.e. }. This behavior is restricted to class templates. The compiler matches vector<bool> automatically with this specialization. The current model for templates is that all source code including definitions goes into the header files. pointer of derived class to pointer of base class. how we nest the two  template declarations. but for booleans we could do better with a bit  vector. if the types T1 and T2 would be assignable from one pair to the other pair. the process of using a function template forces the compiler to instantiate the template.U2>& p). in C++ are several automatic conversions possible. Since we have already mentioned pattern matching for function  file://C:\Users\rmanoj\Desktop\Untitled. second( p.. T2 second.U2>& p) : first( p. It would be nice.T2>::pair( const pair<U1.second) {} Specialization and Partial Specialization Let us assume we have a generic vector class vector<T> that is just fine for the general case. etc.T2). the item type of the list needs to be comparable. template <class T1. but hidden by a default setting). However. In consequence.  Member functions of class templates are also instantiated implicitly. The default copy constructor allows the construction of a pair. which is more precisely  called implicit instantiation. as long as we do not try to sort this list somewhere in our program.Allocator> { // partially specialized implementation }. a tuple (first. so our specialized member function realizes. But lists in general are also useful for types that are not comparable.   template <> struct vector<bool> { // specialized implementation }. Thus. The actual construction of its member variable compiles only if this construction is permitted. vector has a second template argument for a memory allocator (which it does in the STL. Specialization and partial specialization work also for function templates. class T2> struct pair { T1 first. template <class Allocator = std::allocator> struct vector<bool.second) with types (T1. The empty template declaration was previously superfluous. There is also an explicit instantiation.T2> contains a member variable of type T1 and a member variable of type T2. but  is now mandatory. And the compiler is not allowed to complain about the missing comparison operator in the sort member function. The solution is a template constructor accepting all pairs. We can write a specialized class for booleans. but this is not available in most compilers today. which we do not use and do not mention any further.T2> from the STL makes use of a member template constructor. The resulting partial specialization is still a template. However. for example.   Now suppose. i. since C++ is not allowed to compile the sort member function if it is not instantiated somewhere. For a sort function. if the types for the template arguments are exactly the same.htm 1/13/2010 . class U2> pair<T1. Assume that we want to add a sort member function to our list template from above. for example mutable to const. class T2> template <class U1. inline would be preferable here). Member Templates Member functions are basically normal global functions (with some syntactic sugar and compiler support for the this pointer). (see also pair. short to int. The class pair<T1. class U2> pair( const pair<U1.

int main() { list<int> ls. virtual ~Shape() {} }. and   3. is not used in a list of base-classes or as an item to be initialized by a constructor initializer list. the correct examples is:   template <class Container> struct X { typedef typename Container::value_type value_type. C++ supports two kinds of polymorphism: dynamic  (runtime) polymorphism through virtual functions and static (compile time) polymorphism through templates. how the resulting overloading of the function name gets resolved. one cannot  just liberally sprinkle code with typenames. we use a typedef and the same name in class X. Now class X needs the value type T of the container. The keyword typename is used to indicate that the name following the keyword does in fact denote the name of a type. For convenience. and   2. one must use the keyword typename in front of a name that:   1. // . // no typename ==> multiplication! } X<int>::C *s_. it contains a scope operator `::'. one is only allowed to use the keyword in this sense if the first four apply. struct Circle : public Shape { void draw().e.. classes can also contain enum's and types. }. before actually instantiating the template? It does not. is qualified: i.. typename needed X<T>::D *q.   template <class T> struct list { typedef T value_type. because of 4 typename X<T>::Base(0)) {} // typename needed X<T> f() { // no typename. More precisely. However. such as list<T>. Dynamic polymorphism is in  central role in object-oriented programming while static polymorphism is at the heart of generic programming. But how can the compiler know that Container::value_type is actually a type and not a static member variable without knowing the  actual type for Container. However. // not correct // . we can say so explicitly with the new  keyword.. i. it is reported as an ambiguity error. To illustrate this rule.htm 1/13/2010 . consider this code fragment:   template<class T> struct S : public X<T>::Base { // no typename. // declaration of pointer p. it needs to be clarified.. and   5.   template <class Container> struct X { typedef Container::value_type value_type..e.   Let us look at the shape example we saw earlier: struct Shape { virtual void draw() = 0. and   4. list<int>::value_type i. file://C:\Users\rmanoj\Desktop\Untitled. By  default. because of 4 S(): X<T>::Base( // no typename. If it is a type. struct Square : public Shape { void draw(). // no typename. which we have already  prepared with the typedef in the list class template.  Local Types and Keyword typename Besides variables and member functions. has a component left of a scope resolution operator that depends on a template parameter. because of 2 Dynamic and Static Polymorphism Polymorphism refers to the ability of a single piece of code to work with multiple types. }. The solution is the new keyword typename. }. // typename allowed but not needed }..   Furthermore. If there is more than one `most specific' instantiation. The bad news here is  that a sound type theory as known from functional languages is missing here.Page 11 of 72 templates. Thus. struct U { X<int>::C *pc_. }. it might not be such a surprise. }. They are accessed with the scope operator `::'. // is of type int } Let us assume a class X uses a container. and it chooses the  `most specific' instantiation. because of 2 typename X<T>::C *p. the compiler assumes that in case of such ambiguities the token is not a type. appears in a template.  The general rule of thumb is that the compiler tries to instantiate all function templates that can match the function call. denotes a type.

 Other problems with dynamic polymorphism are:   Built-in types cannot be handled directly. T& b) { T tmp = a. a drawing class might have a list of its component shapes:   • • • • class Drawing { list<Shape*> components. Compilation of display<T> needs the definition of T.)   Static type checking is compromised. dynamic polymorphism is more appropriate. CGAL_.htm 1/13/2010 .   Name Spaces As a library developer we do not own the universe.h" defines typedef unsigned char Byte.Page 12 of 72 }.   Inefficiency. for identifier names. } void swap (Swappable& a. or Byte have been defined in more than one library with surprising results when those libraries where used together. trying to swap objects of two different types cannot be detected at compile time. Now we have two ways of writing a single function that works for circles. There is a separate instance of display<T> for each type T..assign(*tmp). virtual ~Swappable() {}.h" contains the macro definition #define Byte unsigned char.assign(b). and it is supposely different than all other prefixes. They can be extended anytime. for example. This is clearly more awkward  to use than the swap template.. we need a base class with virtual  versions of copy constructor and assignment. To do this with virtual functions. The prefix is typically a short abbreviation such as std_. Q for the Qt GUI interface. and any other classes derived from Shape:   void display (const Shape& s) { s. class A. The problems with static polymorphism are:   Heterogeneous dynamic collections are difficult to handle. Let us look at the swap example:   template <class T> void swap( T& a. It frequently happened that common names such as min. // . An application programmer most likely uses more than one library and create additional identifier names. max. and another header file "b. For example. Swappable& b) { Swappable* tmp = a. virtual Swappable& assign(const Swappable& rhs) =0.  we use normal member functions:   struct Swappable { virtual Swappable* clone() const =0. 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 basic operations used by swap are copy constructor and assignment. delete tmp. In C++ we have the new concept of name spaces. no inlining. void foo( const A& a). } Now swap works with any type that is derived from Swappable and defines clone and assign appropriately. } template <class T> void display (const T& s) { s. Because a constructor cannot be virtual and virtual assignment has its problems (see [Gillam98]). b.clone().draw(). Assume a header file "a. int b). swap. What happens if we include both header files.   Templates have their advantages. too. For example. a. } // ends the namespace CGAL file://C:\Users\rmanoj\Desktop\Untitled. } // relies on dynamic polymorphism Code bloat. (Virtual function call overhead.draw(). squares.   No separate compilation. a = b. b = tmp. They act as a scope and group identifiers together. An example: namespace CGAL { int max( int a. } // dynamic polymorphism // static polymorphism // T does not need to be derived from Shape In this case.

the name space scope CGAL:: with the so-called scope operator :: has just replaced the prefix in user code. Note that this replacement happens on a text processing basis only. We discuss only a few aspects of the preprocessor. It became  widely recognized as the report [Stepanov95] started circulating in the C++ standardization committee. This is well known in C++ under the name Koenig lookup. the preprocessor does not know about classes. using std::vector. namely constants and templates. Symbolic Constants One can define symbolic constants. largely because it has almost become superfluous with the new language elements of C++. set. within the name space itself we don't have to repeat the scope all the time. A good up-to-date introduction and reference for the STL can be found in [Austern98]. Name lookup happens following the name space scope from the inside to the outside. 4). } We just omitted the scope. but despite that it is still called the  STL. The goal of generic programming is to express algorithms and file://C:\Users\rmanoj\Desktop\Untitled. However. A a. and with their systematic organization. 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. where the identifier following the #define gets literally replaced with the text following it.14159265358979323846 #define CGAL_CFG_NO_KOENIG_LOOKUP 1 Each of these definitions defines a replacement rule. for example. C Preprocessor: Include Guards and assert Macro C++ has inherited from C its preprocessor. There is an alternative possebility for calling the function foo. and  map. data structures. but not all templates in the standard  library belong to the STL.htm 1/13/2010 . but now the compiler also examines the argument types and includes the scopes of the argument types for name lookup searches. assert Macro 3. The reference part is also available online from SGI. Here is one definition [Jazaeri98]: Generic programming is a sub-discipline of computer science that deals with finding abstract representations of efficient algorithms. The STL is part of the C++ Standard Library. // assumes that we have also seen the full definition of A somewhere CGAL::foo( a). such as find and sort. strings are not part of the STL. All the standard C and C++ library has been enclosed in the std namespace. foo( a). vector.  see [SGI-STL]. and it provides basic algorithms. Historically. and other software concepts. These modifications make the standard version incompatible to the first STL. the STL started as an Ada library [Musser89]. } So far. such as: #define M_PI 3. One can import a whole namespace or just selected identifiers into the current namespace with the using declarative: using namespace std.Page 13 of 72 We could use the above declarations in the following way: int main() { int i = CGAL::max( 3. The STL became in a slightly modified  form part of the C++ standard. protection. a separate phase of the compiler that processes its own language before the compiler looks at the (transformed) source code. 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. such as list. It provides basic data types. see this example instead: int main() { A a. STL and Generic Programming Introduction The Standard Template Library (STL) falls into the class of foundation libraries.   The programming paradigm underlying STL is called generic programming.

i. should give you the same as x = y.e. but also an advantage. The function could have been implemented using a default constructor  and assignment.  We can distinguish between syntactic requirements and semantic requirements. swap:   template <class T> void swap( T& a. interoperable form that allows their direct use in software construction. One aspect is to reduce the interface to the data types used in the  algorithm to a set of simple and general concepts. Common basic concepts Concept   Assignable   Syntactic requirements   copy constructor   assignment operator   equality and inequality operator   order comparison with operators <. there is the important difference that concepts are nowhere explicitly coded in the language. the flexibility is resolved at compile time which gives us the advantages of strong type checking and inline efficiency where needed. In general.htm 1/13/2010 . when none dominates the others in efficiency for all inputs. Iterators  file://C:\Users\rmanoj\Desktop\Untitled. In our example. because it avoids the coupling of a common base class.. but the copy constructor is more likely to exist than the default constructor (given that the assignment operator is required  anyway). Of course. In analogy to the object-oriented paradigm. <=.g. compilation can only succeed  if this actual type has an assignment operator and a copy constructor. x = tmp.   Providing more than one generic algorithm for the same purpose and at the same level of abstraction. >=. } When the template is instantiated (by calling the function) the placeholder T becomes an actual type. b = tmp. The syntactic requirements are the assignment operator and the copy constructor in our example. Remember that these are user defined functions. This is a maintenance disadvantage. Key ideas include:   Expressing algorithms with minimal assumptions about data abstractions.   When the result of lifting is not general enough to cover all uses of an algorithm. should be side effect free. One of them is the Iterator concept in STL which is an abstraction of pointers. and > Default Constructible  default constructor   Equality Comparable   LessThan Comparable   A regular type is one that is a model of Assignable. Lifting of a concrete algorithm to as general a level as possible without losing efficiency. T& b) { T tmp = a. e. The semantic requirements are that the copy constructor and the assignment operator should actually copy the values. we may assume that now x == y true is. the generic data structures and algorithms can be parameterized with a base class used in the object-oriented programming to get the runtime flexibility. linking. concepts factor out common signature and behavior for template arguments. it is convenient to group them in often used combinations. Default Constructible. If an actual type fulfills the requirements of a concept.Page 14 of 72 data structures in a broadly adaptable. but ensuring that the most efficient specialized form is automatically chosen when applicable. Generic Algorithms Based on Iterators Algorithmic abstraction is a key goal in generic programming [Musser89]. int is a model of the concept Assignable. In general. and vice versa. However. and models correspond to derived classes. They are only communicated in documentations. thus making them as interoperable as possible. If runtime flexibility is needed.   Concept and Model Consider our first example of a function template. for example. Instead of documenting requirements always in all detail. the most abstract form such that when specialized back to the concrete case the result is just as efficient as the original algorithm. and one in which these expressions interact in the expected way. etc. Equality Comparable. and in general should behave according to the C object model.. A common base class needs a header file and all derived classes have to agree on this single header file. concepts correspond to virtual base classes. We call these collections of requirements concepts. the function has then to be implemented using only the operations specified in the concept. for x = y. One can think of a concept as the `greatest common denominator' of all types for which a function template is supposed to work. This introduces the necessity to provide sufficiently precise characterizations of the domain for which each algorithm is the most efficient. If an actual type fails to comply with these requirements a compilation error points that out. Semantic requirements are not checkable at compile time. a = b. However. tmp = y.. additionally providing a more general form. The concept for the swap function parameter is called Assignable. it is a called a model for this concept.

. 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... initialize elements of a. If we want to copy the 100 elements into a list that is empty at the  beginning.. typedef . also known as  container class in STL.Page 15 of 72 serve two purposes: They refer to an item and they traverse over the sequence of items that are stored in a data structure.. int a2[100]. operator++() . } This generic contains function can be used with C-pointers referring to a C-array... a+100. A generic copy function copies the values of an iterator range to a sequence starting where another iterator points to. The list class template example from the previous section can be extended as follows. . // . We can also search only a part of an array. . see [ISO-C++-98. we cannot use the begin() iterator of the list. operator+=(). The syntactic requirements are only sketched here.  according to the different possibilities of accessing items in a container class. they use iterators instead. The following example declares an array of a hundred integers and searches for a 42.   operator+(). return result. class OutputIterator> OutputIterator copy( InputIterator first.. For an empty list the begin() iterator is actually equal to the end() iterator. though we leave the actual implementation of the iterator open. // . } Lets copy 100 elements from an array of integers to another array of integers. initialize elements of a1. const T& value){ while ((first != beyond) && (*first != value)) ++first. . bool in_third_quarter = contains( a+50. Default Constructible   Syntactic requirements   operator*()   operator->()   operator++().   Bidirectional Iterator  Forward Iterator   Bidirectional Iterator.   bool in_first_half = contains( a. For example.   template <class InputIterator.   int a[100]. It returns true iff the value is contained in the values of the range  [first. 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. insert some elements into ls. a+75. Generic algorithms are not written for a particular container class in STL. Output Iterator. a+50. ls. class T> bool contains( InputIterator first.  The following table shows the different iterator concepts and the refinement relation between them and the basic concepts (see above). forward. InputIterator beyond. which is more general than an input iterator.end()..   template <class InputIterator. This generic contains function can also be used with our list class template as illustrated in the following example:   list<int> ls. 42). iterator. A container class is supposed to provide a member type called iterator. // .. }. operator-().   int a1[100]. template <class T> class list { void push_back( const T& t). iterator begin(). output.  file://C:\Users\rmanoj\Desktop\Untitled. LessThan Random Access Iterator  Comparable   Sequences of items are specified by a range [first. Five different categories are defined for iterators: input.beyond) of two iterators. but it does not include beyond.   . Equality Comparable   Trivial Iterator  Assignable   Input Iterator. The iterator beyond is also referred to as the past-the-end position. a generic contains function  can be written to work for any model of an input iterator..  operator[]().. OutputIterator result){ while (first != beyond) *result++ = *first++. bool found = contains( a. // append t to list. copy( a1.beyond). a2). iterator end().. 42).. SGI-STL] for the full requirements. The copy function is writing over the already existing elements in a2. return (first != beyond). a1+100.   operator--(). 42).begin()... 42)..   operator*(). The usual C-pointer referring to a C-array is a model for a  random-access iterator. bidirectional and random-access iterators. Concept   Trivial Iterator   Input Iterator  Output Iterator  Forward Iterator  Refinement of   Assignable. InputIterator beyond.htm 1/13/2010 .. The copy function  returns an iterator pointing to the past-the-end position of the target sequence after copying. Recall that C-pointers are a model for a random access  iterator. which  is not dereferenceable. which is a model of the Iterator concept. bool found = contains( ls.

} // Trivial Iterator: const T& operator* () const { return t. Using the concept of lazy evaluation from functional programming languages we can also imagine iterators representing more complex and potentially infinite sequences. Own adaptors for  other concepts are easy to add.C) int a[100].   Concept   Generator   Unary Function   Refinement of   Assignable   Assignable   Syntactic requirements   function call. } bool operator!=( const Const_value<T>& cv) const { return !(*this == cv). Note that operator!= and operator++(int) are implemented in terms of other member functions of the iterator. the adapter is a model of an output iterator. for example. } // Input Iterator Const_value<T>& operator++() { return *this. copy( a1. The following example reads integers from the standard input  stream and writes them to the standard output stream. such that a call to this member  function of the object looks like a function call. here the list. Const_value<int> cv( 42).. we might be interested in a finite subsequence. istream_iterator<int>().   copy( istream_iterator<int>(cin). However. In this example. public: // Default Constructible ! Const_value() {} explicit Const_value( const T& s) : t(s) {} // Assignable by default. one argument: Result operator()(Arg1)   function call. Note that copy_n is not part of the C++ standard. } Const_value<T> operator++(int) { Const_value<T> tmp = *this. they compute the sequence from a small internal state.htm 1/13/2010 . 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.   template <class T> class Const_value { T t. 100. and ranges. to append a new element to the end of this container class whenever an element is written to the iterator. The whole is more than the sum of its parts. // fills a with 100 times 42. can be infinite. no arguments: Result operator()()   function call. The concepts in the STL and the adaptors form an extremely flexible toolkit. We will see later on how this back_inserter adaptor is actually implemented. Most adaptors are small classes and function. and it uses a model of a container class.  Other examples for such simple input iterators are a counting iterator and a random number generator. all  other member functions are generic. "\n")). Arg2)  Binary Function   Assignable   file://C:\Users\rmanoj\Desktop\Untitled. } const T* operator->() const { return & operator*(). copy_n( cv. two arguments: Result operator()(Arg1. } }. A first example would be an  iterator to a constant value. they  are unnecessarily complicated. this idea works best with input  iterators that generate the sequence on the fly. There are also adapters to interface between C++ I/O streams and iterators. a1+100. The istream_iterator with the  empty parenthesis denotes the past-the-end position for this range. But in general. copy_n solves this. Here. ostream_iterator<int>( cout. which is the end-of-file condition for the stream. Instead. there is no point in copying an infinite sequence. // Equality Comparable (not so easy what that should mean here) bool operator==( const Const_value<T>& cv) const { return ( this == &cv). back_inserter(ls)).e. only a small subset of the member functions needs to be implemented for a new iterator. For technical reasons.  A First Partial Implementation of an Iterator The stream iterator adaptor example makes a point: Streams. list<int> ls. Function Objects A function object basically is an instance of a class with the operator() member function implemented. ++*this. (see also Const_value. each integer followed by a carriage return "\n". Another generic function. but it is available in most implementations of the STL (or easy to write). the sequence of prime numbers. i. return tmp.Page 16 of 72 The STL provides in these cases small adapters that interface between the concepts. a).

. class Eq> bool contains( InputIterator first. }.  Iterator Traits Iterators refer to items of a particular value type.. a+100. A typical example would be the exchange of the equality comparison with a function object. 42. Note that since function objects are usually passed by value in the STL we store a reference to an external counter and not the counter value  itself in the function objects.. this approach is not sufficient. Eq eq ) { while ((first != beyond) && ( ! eq( *first. as in the following example of an iterator  referring to integer values. 42. First. Algorithms parameterized with iterators might need the value type directly. // counter contains number of comparisons needed. equals<int>()). }. } }. The solution chosen for the STL is the iterator traits class. The value type of the iterator example class above can now be expressed as iterator_traits< iterator_over_ints >::value_type. count_equals<int>(counter)). The advantage of objects is that they can have an  internal state. in our example to one. const T& b) { ++count. } }. we define a function object equals that performs the same comparison. 42. The value type can be referred to with the expression iterator_over_ints::value_type. bool found = contains( a. return (first != beyond). value))) ++first. eps_equals( const T& eps) : epsilon(eps) {} bool operator()( const T& a..   struct iterator_over_ints { typedef int value_type. size_t counter = 0. Since a C-pointer is a valid iterator. return a == b.   template <class T> file://C:\Users\rmanoj\Desktop\Untitled.Page 17 of 72 Predicate   Unary Function   result type is bool   Binary Predicate  Binary Function  result type is bool   Function objects are well suited as parameters for generic functions. } }. value_type. const T& b) { return (a-b <= epsilon) && (b-a <= epsilon). count_equals( size_t& c) : count(c) {} bool operator()( const T& a. // . const T& value. // . a+100.  For C-pointers a specialized version of the iterator traits class exists. which is a class  template parameterized with an iterator:   template <class Iterator> struct iterator_traits { typedef typename Iterator::value_type // .. bool found = contains( a. eps_equals<int>(1)). a+100. The next section illustrates how the additional parameter of the contains function can be automatically selected if the value type of the  iterator is known.   template <class T> struct eps_equals { T epsilon.   int a[100]. template <class T> struct equals { bool operator()( const T& a.   template <class InputIterator.. const T& b) { return a == b. InputIterator beyond. How about a function object that counts the number of comparisons needed as a side-effect? Here it is:   template <class T> struct count_equals { size_t& count. bool found = contains( a. Assuming that  iterators are implemented as classes the value type can be defined as a local type of the iterator. initialize elements of a. The expression  equals<int>() calls the default constructor for the template class equals<int> from above which is a function object comparing two  integers for equality.htm 1/13/2010 . so that the contains function will also return true if the values 41 or 43 do  occur in the range. C++ allows to use also simple function pointers as function objects. The eps value is stored in the function object itself. class T. which is currently hard coded as the operator== in the generic contains function from above. } The example using C-arrays with the contains function needs now an additional argument -- the function object. It needs an additional template parameter Eq and takes an additional  function parameter eq for a binary function object which is used for the comparison. At construction time of the function  object the actual value for eps is initialized. We modify the iterator-based generic contains function from above. We continue our example of the contains function and define a comparison object that is true when the absolute value of the  difference of its two arguments is smaller than eps.

The function adaptors are paired with function templates for easy creation. For example.  template <class Predicate> class unary_negate : public unary_function< typename Predicate::argument_type. typedef Result result_type. class Result> struct binary_function { typedef Arg1 first_argument_type. class Arg2. Here. Now the value type of a C-pointer. bool> { protected: Predicate pred. const T& b) { return a == b.  Implementing Adaptable Function Objects Adaptable function objects require in addition to regular function objects some local types that describe the result type and the argument  types.T. } }. value. The idea is that the function template derives the type for the  template argument automatically (because of the matching types).. beyond. partial specialization  is required. Adaptable Unary Function   Binary Predicate. to int. } STL makes use of traits classes in other places as well. T::first_argument_type. }.Page 18 of 72 struct iterator_traits<T*> { typedef T value_type. typedef Arg2 second_argument_type.   template <class Predicate> inline unary_negate< Predicate> not1( const Predicate& pred) { return unary_negate< Predicate>( pred). char_traits to define the equality test and other operations for a  character type. e. The adaptors need the annotated type information to  declare proper function signatures etc.  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. // . but it cannot be a valid model of an adaptable function object. can be expressed as iterator_traits< int* >::value_type.. In addition. InputIterator beyond. T::argument_type   T::result_type. The definition of binary_function in the STL is as follows:   template <class Arg1. our function object equals from above could be derived from std::binary_function to declare the appropriate types. const T& value) { typedef typename iterator_traits<InputIterator>::value_type value_type. Adaptable function objects can be used with adaptors to compose function objects. #include <functional> template <class T> struct equals : public std::binary_function<T.  T::second_argument_type   Adaptable Binary Function   Binary Function   Adaptable Predicate   Adaptable Binary Predicate  Predicate. } }. The program copies all integers from cin to cout that cannot be divided by the  file://C:\Users\rmanoj\Desktop\Untitled. the pointer type and  the reference type of the iterator. The iterator traits class contains also definitions about the difference_type. model T   T::result_type   T::result_type.g. }. this character traits class is used as a template parameter for the basic_string class template. typedef equals<value_type> Equal. } A short program in [Stepanov95] makes use of this negater.. A function pointer can be a valid model for a function object.htm 1/13/2010 .bool> { bool operator()( const T& a.  Concept   Adaptable Generator   Adaptable Unary Function   Refinement of   Generator   Unary Function   Syntactic requirements. for example. the iterator_category. which allows the  adaption of the string class to different character sets.C) template <class InputIterator. An examples is the negater unary_negate that takes an unary predicate and is itself a model for an  unary predicate. Equal()). but with negated boolean values. return contains( first. Adaptable Binary Function           Small helper classes help to define adaptable function objects easily. class T> bool contains( InputIterator first. public: explicit unary_negate( const Predicate& x) : pred(x) {} bool operator()(const typename Predicate::argument_type& x) const { return ! pred(x).

copy( a1. (see also remove_if_divides. pointer. it returns the  value of the binary function object called with its argument and its internally stored value as second argument.  list<int> ls. class Tp> inline binder2nd< Operation> bind2nd( const Operation& fn.   template <class Operation> class binder2nd : public unary_function< typename Operation::first_argument_type. const Tp& x) { typedef typename Operation::second_argument_type Arg2_type. public: binder2nd( const Operation& x. } back_insert_iterator<Container>& operator*() { return *this.   template < class Operation. typename Operation::result_type> { protected: Operation op. is again a small helper function to create an object of type binder2nd. } back_insert_iterator<Container>& operator++(int) { return *this.C)   int main( int argc. value). file://C:\Users\rmanoj\Desktop\Untitled. "\n"). but then  it works). not1( bind2nd( modulus<int>(). There is a similar adaptor called binder1st that binds a value to the first  argument. or encapsulate function pointers and member function pointers in  adaptable function objects.  template <class Container> inline back_insert_iterator<Container> back_inserter(Container& x) { return back_insert_iterator<Container>(x). istream_iterator<int>(). public: typedef Container typedef output_iterator_tag typedef void typedef void typedef void typedef void container_type. reference. these are higher order function objects.htm 1/13/2010 . is evaluated. } The other function object adaptor in this example. So. } An object of type binder2nd stores an adaptable binary function object and a value compatible with the type of the second argument of the  adaptable binary function object. Each time an expression for a  back_insert iterator i of the from *i = value. bind2nd. explicit back_insert_iterator(Container& x) : container(&x) {} back_insert_iterator<Container>& operator=(const typename Container::value_type& value) { container->push_back(value). value(y) {} typename Operation::result_type operator()(const typename Operation::first_argument_type& x) const { return op(x. Whenever its operator is called. } back_insert_iterator<Container>& operator++() { return *this. This is similar to currying known in functional programming languages (it needs much more writing in C++ to make it work. Arg2_type(x)). return binder2nd< Operation>( fn. The object itself behaves then like an unary function object. char** argv) { if ( argc != 2) throw( "usage: remove_if_divides integer\n"). difference_type. } }.  Implementation of the Iterator Adaptor back_inserter A class template that is a model of an output iterator. Other function object adaptors exist that can compose function objects. atoi( argv[1])))). } Here is a short example of its use with a list class.Page 19 of 72 integer parameter given to the program. iterator_category. value_type. ostream_iterator<int>(cout. typename Operation::second_argument_type value.  template <class Container> class back_insert_iterator { protected: Container* container. } }. a1+100. It keeps a reference to a container class as internal state. return *this. back_inserter(ls)). return 0. A small helper function template provides again the convenience not to type the template arguments explicitly. This adapter binds a value to  the free variable of the second argument of a binary function object. the value is appended to the container class using the push_back() member  function. const typename Operation::second_argument_type& y) : op(x). remove_copy_if( istream_iterator<int>(cin).

In principle the compiler could perform this optimization. forward_iterator_tag : public input_iterator_tag {}. InputIterator last. InputIterator last) { typedef typename iterator_traits<InputIterator>::iterator_category Category. like we do with function objects and binary_function<Arg1. Now we can implement a generic distance function (original implementation as it is in  the STL):   template <class InputIterator> inline typename iterator_traits<InputIterator>::difference_type __distance( InputIterator first. random_access_iterator_tag) { return last .h and Iterator_base. Category()). Obscure reasons about multiple derivation kept this derivation out of the standard. However. The  example in Iterator_base. for example. This category can used to select different algorithms.Result>. output_iterator_tag {}.Arg2. but based on the Barton-Nackman trick  Function Dispatch using Iterator Category at Compile Time An iterator belongs to a specific iterator category. but. this  derivation isn't likely to simplify real implementations anyway. { i.. struct struct struct struct struct input_iterator_tag {}. An iterator is assumed to have a local type iterator_category that is defined to be one of these tags. allocation. bidirectional_iterator_tag : public forward_iterator_tag {}. while (first != last) ++first. This iterator category is accessed using iterator traits. but can only be computed in linear time (by counting) for all other  categories. {}. return __distance(first.Page 20 of 72 More Iterators See Iterator_identity. return 0. last. the forward_iterator_tag should be derived also from the  output_iterator_tag. file://C:\Users\rmanoj\Desktop\Untitled. RandomAccessIterator last. } template <class RandomAccessIterator> inline typename iterator_traits<RandomAccessIterator>::difference_type __distance( RandomAccessIterator first.  The C++ standard defines five empty classes to denote the different iterator categories.h and Iterator_identity. ++n. cout << "size of C = " << sizeof(C) << endl.first. int main() { cout << "size of A = " << sizeof(A) << endl. g++ does not. even of an empty class.  These tags are quite convenient to annotate symbolic information at compile time. } Note how the class hierarchy among the iterator tags is used to reduce the number of overloaded functions __distance that need to be  implemented here.C for an adaptor class that takes an iterator and behaves itself exactly like this iterator.htm 1/13/2010 . // .. if we derive from an empty class. An object has always non-zero size. However. we would like to avoid any size penalties.  struct Some_iterator { typedef forward_iterator_tag iterator_category. The following program shows the effect. there is a catch. On the other hand. These types will be used as symbolic tags at compile time. Following the refinement relation of the iterator concepts. random_access_iterator_tag : public bidirectional_iterator_tag {}. class C int }. arrays. input_iterator_tag) { typename iterator_traits<InputIterator>::difference_type n = 0. cout << "size of B = " << sizeof(B) << endl. This is reasonable (the address identifies an object) and helps defining invariants about size. etc. For example the difference between  two iterators can be computed in constant time for random access iterators. } template <class InputIterator> inline typename iterator_traits<InputIterator>::difference_type distance( InputIterator first. : public A { i. class A class B int }.C implements the same adaptor. return n. }. #include <iostream> using namespace std.

5. S = 4. Conventional library interfaces as DSLs are restricted with respect to:   syntactic and semantic extensions   domain-specific optimizations   domain-specific error checking   In C++. R1 = 1. and domain  implementation. 0. Identification and specification of the overall implementation architecture   2. The domain engineering method DEMRAL  [Czarnecki00] divides domain design into three activities:   1. 4.   Expression DSLs: Used for writing expressions involving the library data types. 1. Design Strategies Domain Design The domain engineering process (see Generative Programming) consists of three main tasks: domain analysis. is to develop the architecture. R2(3. 4. 5. 3). 3. SymmMatrixType S(3. template metaprogramming offers a tool to address these limitation. symm<> > >::RET SymmMatrixType. DSLs range from separate special languages such as SQL to  implicit language extensions such as a library user interface. DEMRAL has two kinds of DSLs: Configuration DSLs: Used for specifying the configuration of data types and algorithm. typedef MATRIX_GENERATOR< matrix< double. domain design. 1. 0. The specification is then  translated into a concrete configuration expressed in implementation components configuration language (ICCL). 3).Page 21 of 72 } 4. 3). Specification of the configuration knowledge   It involves the following tasks:   Scope domain model for implementation   Identify packages   Develop architectures and identify implementation components   Identify user DSLs   Identify interactions between DSLs   Specify DSLs and their translation   Domain Specific Languages A domain specific language (DSL) is a specialized problem-oriented language. domain design. Identification and specification of domain-specific languages (DSLs)   3. (see Expression Templates)   Czarnecki00]:  // specify matrix configurations using a configuration DSL typedef MATRIX_GENERATOR< matrix< double. --> size=2 width="100%" align=center>   Configuration DSLs A user of a library specifies the configuration of a library data structure or algorithm using a configuration DSL. 2. 6.   Generative programming emphasizes multiple modular. composable DSLs instead of single monolithic DSL. R2 = (R1 + S) * R1. 2. rect<> > >::RET RectMatrixType. The purpose of the second step.htm 1/13/2010 . 4. 6.   file://C:\Users\rmanoj\Desktop\Untitled. // matrix RectMatrixType R1(3. 1. 0.

Assuming WidgetManager always wants a creation policy for objects of type Widget. Most libraries do not make a clear separation between DSL and ICCL. which are called policy classes.   Policy-Based Class Design Policy-based class design [Alexandrescu01] is a component-oriented architectural design technique.Page 22 of 72   DSL and ICCL serve different purposes. The main class or host class obtains the  functionality of a policy by getting a policy class as a template argument. which can be complex. We have also a host class that uses the policy. configuration is  usually directly specified by the user. DSL is designed to provide a convenient interface for the user of the library. return new(buf) T.   typedef WidgetManager<> DefaultWidgetManager..     As an example.htm 1/13/2010 . while DSL allows specification using different levels of detail: no details (use defaults)   usage profile (for example. This also allows the WidgetManager to create objects of other types using the same policy: file://C:\Users\rmanoj\Desktop\Untitled. ICCL is designed to allow flexible. This allows independent evolution of the user code and the  implementation component code. This separation has a central role in generative programming. reusable implementation components. typedef WidgetManager< MallocCreator<Widget> > MallocWidgetManager. where the translation is performed using  generators. It decomposes the functionality of a class  into policies. optimize for space or time)   direct specification of components   user-provided implementation   Having both DSL and ICCL separates the problem space and the solution space. Now the user is able to select the way WidgetManager creates objects. template <class T> struct MallocCreator { static T* create() { void* buf = std::malloc(sizeof(T)). Here are two implementations: template <class> struct OpNewCreator { static T* create() { return new T.   template <class CreationPolicy = OpNewCreator<Widget> > class WidgetManager : public CreationPolicy { // . if (!buf) return 0. requiring the user to specify this is redundant and even dangerous.. let us define a policy for creating objects. } }. For example in STL. we can use template template parameters. To avoid this. } }. ICCL describes the full configuration. Each policy can have multiple implementations. }.

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
A creation policy class is required to have a member function create, but it can also have additional functionality:  
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; };

The host class inherits this additional functionality, and the user can take advantage of this:  
typedef WidgetManager<PrototypeCreator> MyWidgetMgr; // ... Widget* p = ...; MyWidgetMgr mgr; mgr.set_prototype(p); //...

The "lazy" implicit instantiation of member functions of class templates enables even WidgetManager itself to use the additional  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); } // ... }; User can still create objects of type WidgetManager<OpNewCreator> even though OpNewCreator does not have a member functions  get_prototype and set_prototype. A compiler error will occur only if the user tries to call switch_prototype.  

Combining Policies
The power policies becomes more apparent when there are multiple policies. The Loki library [Alexandrescu01] contains an implementation of  smart pointers as a host class with four policies:  
template < class T, template <class> class class template <class> class template <class> class > class SmartPtr; OwnershipPolicy ConversionPolicy CheckingPolicy StoragePolicy = = = = RefCounted, DisallowConversions, AssertCheck, DefaultSPStorage

Each policy has multiple implementations in Loki:   Ownership policy: DeepCopy, RefCounted, RefCountedMT, COMRefCounted, RefLinked, DestructiveCopy, and NoCopy.   Conversion policy: AllowConversion and DisallowConversion.   Checking policy: AssertCheck, AssertCheckStrict, RejectNullStatic, RejectNull, RejectNullStrict, and NoCheck.   Storage policy: DefaultSPStorage, ArrayStorage, LockedStorage, and HeapStorage.   Altogether this gives 7x2x6x4=336 combinations using 1+7+2+6+4=20 components. That is, 336 different smart pointer classes! Furthermore,  later version of the library could add new policy classes and users can define their own policy classes.  

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;

A technique for implementing such named template arguments is described in [Vandevoorde03, Section 16.1]. Let us consider a simple  example: a BreadSlicer class with three policy parameters Policy1, Policy2 and Policy3. The BreadSlicer itself is defined as follows. 
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 // ... }; Here BreadSlicers template arguments are policy setters instead of the policies themselves. The policy setters can be defined like this.   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, the PolicySelector extracts the actual policies from the policy setters.   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> {}; The purpose of the Discriminator template is to avoid an error when two or more of the setters are the same class (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

GenVoca is a software architecture model that is similar to policy-based architectures in that final types are build from components, but the  components are organized differently. In GenVoca the components are not typically policies that are plugged in to implement some part of the  functionality but wrappers (layers) on top of a more basic components adding functionality to it.  

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] R and S are realms, and A, B, C, D and E are layers. A GenVoca grammar defines an ICCL. Possible configurations of the model can be described  by GenVoca expressions.For example, valid configurations of the realm S in the above grammar include:  

 

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
As example of a GenVoca, let us design an architecture for a simple list with the feature diagram:  

  More details on this example can be found in [Czarnecki00, Chapter 12].  

Designing a GenVoca architecture consists of the following steps.
1. Identify the main responsibilities in the feature diagram. In our example, the responsibilities include:  storage of elements   copying of elements   destroying elements   dynamic type checking (to ensure monomorphism)   length counter   tracing   2. Enumerate component categories and components per category. There is a component category for each responsibility of the previous  step and a component for each alternative implementation of the responsibility. In the example, the categories and components are:  BasicList: PtrList   Copier: PolymorphicCopier, MonomorphicCopier, EmptyCopier   Destroyer: ElementDestroyer, EmptyDestroyer  

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

1/13/2010

Sort the categories into a layered architecture. Config 10.. we get four layer categories. ElementType 14. Note the circularity in the definition. since this list configuration has no length counter. Write down the GenVoca grammar.  From the above figure. List 7. Note how there is no one-to-one correspondence between the feature diagram and the GenVoca grammar. Counter and BasicList each form their own layer categories.h.   5. Tracing. because the higher layers need the final list type.   Implementing a GenVoca Architecture We will now implement the list described by the above GenVoca grammar. Destroyer. Copier. BasicList 9.)   A user can define a particular configuration by writing a configuration repository. The type ElementArgumentType is used as the argument type  when inserting elements.   Let us then implement the components. This is a very bare-bones implementation concentrating on the architectural aspects. OptCounterList 8. We start with the basic list PtrList: template <class Config_> class PtrList { public: // export Config typedef Config_ Config. It is either ElementType or const ElementType depending on the configuration. The  component categories that do not depend on other categories are grouped into the bottom ConfigurationRepository (Config for  short) layer category. The dependecies of the example are given by the following figure:     4.h and list_example. Tracing and Counter are optional layer categories. EmptyTypeChecker   Counter: LengthList   Tracing: TracedList   3. for example: struct TracedIntListConfig { typedef int typedef const ElementType typedef MonomorphicCopier<ElementType> typedef ElementDestroyer<ElementType> typedef DynamicTypeChecker<ElementType> ElementType. file://C:\Users\rmanoj\Desktop\Untitled. TypeChecker 13. }. The configuration repository is the innermost (bottom) layer of the list but contains the final list type as its  member. Destroyer 12. (See also list_generator. Identify ``use'' dependencies between component categories. For example.C. ElementArgumentType.Page 26 of 72 TypeChecker: DynamicTypeChecker. TypeChecker. Copier is affected  by both Ownership and Morphology features. LengthType : : : : : : : : : TracedList[OptCounterList] | OptCounterList LengthList[BasicList] | BasicList PtrList[Config] PolymorphicCopier | MonomorphicCopier | EmptyCopier ElementDestroyer | EmptyDestroyer DynamicTypeChecker | EmptyTypeChecker [ElementType] int | short | long | . A ``user'' component category is placed in a higher layer category than the ``used'' one.. Copier 11. typedef TracedIntListConfig::FinalListType TracedIntList. This is necessary.htm 1/13/2010 . LengthType is not defined  here.   6. typedef TracedList<PtrList<TracedIntlListConfig> > FinalListType. We need to implement the following components:   TracedList   LengthList   PtrList   PolymorphicCopier   MonomorphicCopier   EmptyCopier   ElementDestroyer   EmptyDestroyer   DynamicTypeChecker   EmptyTypeChecker   The full implementation of the components is in list_components.

Page 27 of 72 private: // retrieve the needed types from the repository typedef typename Config::ElementType ElementType. typedef typename Config::Destroyer Destroyer. List*& l) { l = new List(e. typedef typename Config::TypeChecker TypeChecker.C for more):   template <class List> void print_list(List* l) { std::cout << "[ ". // note: not PtrList* but FinalListType* This primitive singly linked list could be used as follows (see list_example. } ElementType& head() { return *head_. print_list(ls). // data members ElementType* head_. FinalListType *t = 0) : head_(0). tail_(t) { set_head(h). push_front(2.ls).clone(). push_front(3. l). l. } template <class List> void push_front(typename List::ElementArgumentType& e. // prints "3 2 1" } The PtrList delegates some of the work to other components. } void set_tail(FinalListType *t) { tail_ = t. l = l->tail() ) std::cout << l->head() << " ". template <class ElementType> struct PolymorphicCopier { static ElementType* copy(const ElementType& e) { return e. std::cout << "]\n". // polymorphic copy using } // virtual member function clone() }. public: PtrList (ElementArgumentType& h.ls). } FinalListType* tail() const { return tail_. typedef typename Config::ElementArgumentType ElementArgumentType. List* ls = 0. for ( . } void set_head(ElementArgumentType& h) { TypeChecker::check(h). } }. push_front(1. typedef typename Config::FinalListType FinalListType. // no copy } }. } ~PtrList() { Destroyer::destroy(head_).ls). FinalListType* tail_. typedef typename Config::Copier Copier. // note: not const The components for element destruction and type checking are even simpler:   template <class ElementType> struct ElementDestroyer { static void destroy(ElementType* e) { delete e. The way elements are copied (or not) is determined by the Copier which is  one of the following:   template <class ElementType> struct MonomorphicCopier { static ElementType* copy(const ElementType& e) { return new ElementType(e). head_ = Copier::copy(h). } int main() { typedef ListConfig::FinalListType List. } }. template <class ElementType> struct EmptyDestroyer { file://C:\Users\rmanoj\Desktop\Untitled.htm 1/13/2010 . } }. template <class ElementType> struct EmptyCopier { static ElementType* copy(ElementType& e) { return &e.

typedef typename Config::LengthType LengthType. class Then. we need to define the configuration DSL for specifying list configurations. translates the specification into a configuration repository (configuration DSL  to ICCL translation). Morphology {mono. length_ = compute_length(). but leaves a significant part of the final list definition to the writer of the configuration repository. no_counter}. Generators The list implementation described above defines the components but contains little configuration information. the configuration DSL does not allow invalid specifications. cp}. We will represent the features using enumeration types: enum enum enum enum Ownership {ext_ref. template <class ElementType> struct DynamicTypeChecker { static void check(const ElementType& e) { assert(typeid(e)==typeid(ElementType)). class Else> struct IF { file://C:\Users\rmanoj\Desktop\Untitled. CounterFlag {with_counter. TracedList is similar and can be  found in list_components. In general. the higher layers are implemented as inheritance-based wrappers. and produces the final list type as result.htm 1/13/2010 . typedef typename Config::FinalListType FinalListType. The generator takes the set of features as template arguments. LengthType compute_length() const { return tail() ? tail()->length()+1 : 1.Page 28 of 72 static void destroy(ElementType* e) {} // do nothing }. We will next write a list generator to automate the configuration. TracingFlag {with_tracing.h. } }. typedef typename Config::ElementArgumentType ElementArgumentType. and uses a helper  metafunction IF to choose types based on boolean constants (see template metaprogramming for more about metafunctions):   template <bool condition.   First. The generator does the translation at compile time.   A generator takes a possibly incomplete requirements specification written in a more convenient configuration DSL and produces the finished type. private: // retrieve the needed types from the repository typedef typename Config::ElementType ElementType. Finally. own_ref. FinalListType *t = 0) : BasicList(h. This is too  tedious and error-prone task to be left for the user. } LengthType length() const { return length_. template <class ElementType> struct EmptyTypeChecker { static void check(const ElementType& e) {} }. no_tracing}. It is based on the features in the feature diagram rather than the implementation components. For example.   In our example.   template <class BasicList> class LengthList : public BasicList { public: // export config typedef typename BasicList::Config Config. a configuration generator performs the following tasks: complete the specification (compute defaults)   check that the specification is valid   assemble the components into the finished type. poly}. and the only defaults are provided by default template arguments. This makes the components  simple flexible and reusable. } public: LengthList (ElementArgumentType& h. } void set_tail(FinalListType *t) { BasicList::set_tail(t). the combination of MonomorphicCopier and EmptyDestroyer would  likely lead to memory leaks. Only LengthList in given here. t) { length_ = compute_length(). LengthType length_. } }.

typedef typename IF<is_copy. The generator itself is here (and in list_generator. }. no_counter. // define the list type typedef PtrList<Config> BasicList. does_tracing = tracing_flag==with_tracing }. }. OptLengthList >::RET List. is_mono = morphology==mono. EmptyTypeChecker<ElementType_> >::RET TypeChecker_. // define the configuration repository struct Config { typedef ElementType_ ElementType. ElementDestroyer<ElementType_>. LengthList<BasicList>. DynamicTypeChecker<ElementType_>. has_counter = counter_flag==with_counter. typename IF<is_mono. no_tracing. private: // define the constants used for type selection enum { is_copy = ownership==cp. EmptyDestroyer<ElementType_> >::RET Destroyer_.   template < class ElementType_. mono.htm 1/13/2010 . EmptyCopier<ElementType_> >::RET Copier_. public: // return the final list type typedef List RET. typedef typename IF<is_mono. template <class Then. Ownership ownership = Morphology morphology = CounterFlag counter_flag = TracingFlag tracing_flag = class LengthType_ = > class LIST_GENERATOR { public: cp. BasicList >::RET OptLengthList. Then.h). // select the components typedef typename IF<is_copy || is_own_ref. PolymorphicCopier<ElementType_> >::RET.Page 29 of 72 typedef Then RET. typedef typename IF<is_copy. class Else> struct IF<false. is_own_ref = ownership==own_ref. TracedList<OptLengthList>. ElementType_ >::RET ElementArgumentType_. const ElementType_. typedef typename IF<has_counter. file://C:\Users\rmanoj\Desktop\Untitled. Else> { typedef Else RET. MonomorphicCopier<ElementType_>. int // forward declaration of the configuration repository struct Config. typedef typename IF<does_tracing.

~Init(). Finding a feasible order would be too hard. 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. proxy classes. but they were desperate. The developers were  well aware of their sin. Now we can specify a list configuration TracedIntList we saw earlier as follows:   typedef LIST_GENERATOR<int. Halting-Problem equivalent [Item 47. its destructor performs housekeeping.cp. typedef RET }. even none with the Cpreprocessor. breaking cyclic type dependencies with templates.htm 1/13/2010 . An effective but expensive solution would be opaque pointers and a link library manipulating the opaque pointer. if the initialization isn't trivial. 5. public: Init(). But no function call can be triggered automatically in C. Automatic Library Initialization and Housekeeping In C Initializers of global and static variables are automatic. we discuss usually solutions that protect against Murphy. Destroyer. and  housekeeping is performed once the last compilation unit destructs the static init_var (see also [Page 640.no_counter. Thus we write a  header file with the following declaration and include it in each header that relies on the library being initialized. C++ initializes always non-local  static objects. and  double dispatch. housekeeping has also to be implemented as an explicit function call. Note.mono. The order of initialization for  non-local static objects is unspecified. Copier. Its default constructor initializes the library.Page 30 of 72 typedef typedef typedef typedef typedef ElementArgumentType_ Copier_ Destroyer_ TypeChecker_ LengthType_ ElementArgumentType. as C-style type casts and the example above illustrates. Libraries have to be initialized explicitly. The library is initialized for the first compilation unit. Otherwise. The static member variable  count counts now how many different compilation units are initialized. it is useful to remember that all the protections in C++ require the cooperation from the user.  However. Stroustrup97] for this solution). However. }. where the sources of the underlying data are not published. Meyers97] (this  reference describes also how to get around this restriction with local static variables in global functions). 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 The standard does not guarantee that this really works. in fact.  Depending on the C runtime library. Murphy describes an user that makes occasionally mistakes. More examples can be found in list_example. If no tracing was required. an explicit  housekeeping function might be appropriate to avoid the uncertainties about when the destructor gets finally called. even if they are not used. but it is pretty effective and works probably with most compilers. The only restriction of  this solution is that the initialization cannot rely on the initialization of static member variables of other classes. file://C:\Users\rmanoj\Desktop\Untitled. FinalListType. and protection against Macchiavelly. we could simply write   typedef LIST_GENERATOR<int>::RET IntList. Advanced C++ Programming Introduction C++ provides many ways of abstraction.  We can distinguish between to kinds of protection a design can provide: protection against Murphy. while Macchiavelli describes an user that willingly tries to get around the protection mechanism. LengthType. ways to shield the user from implementation details. TypeChecker. const correctness.c) In C++ We use a static member variable.C. Here. Protection against Macchiavelli in C++ is almost impossible. (see also housekeeping.with_tracing>::RET TracedIntList. We will discuss several important concepts in this  chapter: library initialization. varies smart pointers. a technique to make a function `virtual' for two arguments simultaniously.  #ifndef INIT_H #define INIT_H class Init { static unsigned int count. But they have to be in a compilation unit that actually gets linked to the executable.

the data it refers to --------------------------------------int* p. for const int* const q I start with q the variable. i++ ++. L-values can be used for the left side of an assignment.   const int i = 5.)   Const Declarations in C and C++ A const declaration of a variable forbids changes of the variable after its initialization.Page 31 of 72 }. but we make it a class template with a dummy  template parameter which allows us to move the static member variable definition from the object file into the header file. However. and finally the left const declares  the value of type int to be constant as well. // the post increment operator Now. Make Temporary Return Objects in C++ Classes Const Member functions of classes in C++ have a hidden parameter this. For built-in data types C and C++ distinguish between l-values and r-values. A() : i(42) {} }. they can only be initialized with the constructor initializer syntax. As well. // second ++ forbidden! For classes in C++ we need to model this behavior explicitly using const declarations. R-values cannot be used for the left side of an assignment. they are  non-const. non-const non-const int* const q. we cannot write:   int i. const makes it constant. We implement also  the constructor and the destructor to maintain the counter and to perform the initialization and housekeeping. not in the constructor call. Here is the table of all four combinations:   the pointer. And both can be combined. but the value it refers to can change. lets try:   file://C:\Users\rmanoj\Desktop\Untitled. a pointer  can be declared to point to a constant value. const const I read these declarations from the inside out. // violates const declaration A pointer can be declared const as well. // i = 6. }. non-const const const int* const s.h in the library example lib/). or it is of type const T* const if the member function is declared const. Init::Init() { // default constructor if ( 0 == count++) { // perform initialization } } Init::~Init() { // destructor if ( 0 == --count) { // perform housekeeping } } For a template library which consists only of header files. namespace { static Init _init_var. }. thus a constant pointer.  // // // // // // Member variables in classes can be declared constant as well. An example: struct A { const int i. #endif // INIT_H // There is a catch with this solution if we have a template library that does not have any object files for linking -- we need to link with an object  file that contains the definition of the static member variable count and this one must only exist in one compilation unit. For a class C. void bar() const.h and Init. // hidden parameter: T* const this. Consider the following class:   struct A { A operator++ (int).   struct C { void foo(). but is itself an r-value. }. Thus. thus the value of the pointer cannot change. this parameter is of type T* const if the member function  is declared non-const. const non-const const int* r. // Unnamed namespace can be used to avoid name collisions. They are  const.\  Const Correctness (See also [Item 21 and 29. They can only be used for the right side of an assignment. *  makes it a pointer.htm 1/13/2010 . For example. For example the post-increment operator requires an l-value.C for  a full implementation in the library example lib/)  unsigned int EX_Init::count = 0. Meyers97]. (see also EX/Init. // hidden parameter: const T* const this. we can basically use the same class. int makes it a constant pointer referring to a value of type int. // Trigger constructor and destructor calls in each compilation unit. (see  EX/Template_init.

 that works. It works. if the referenced value is considered to be part of the observable state. file://C:\Users\rmanoj\Desktop\Untitled. because a++ returns a temporary object of type A.. // some more functions }... In the example of the string class. The status of the cache cannot be observed from the outside (except in the runtime difference). The implementor of the string class explicitly has to take care (and can take care) that this cannot happen. We implement both member functions twice.Page 32 of 72 A a.get_pointer() = buf. Since the  second ++ works on a temporary object. a user of a class expects conceptual constness which means that the value of an object cannot change its  observable state if it is declared const. valid(true) {} char& operator[]( int idx) { valid = false. l(0). the example from above works. public: string() : s(0). and once for the nonconst case..e. s[1] = 'e'. l(0). return s[idx]. mutable size_t l. mutable size_t l.. does not change string conceptually . An example would be a string class that maintains a cache of the string length.   struct A { const A operator++ (int). size_t l. Changing the  class definition as follows. the pointer s cannot change. } The new keyword mutable solves the problem here. On the other hand. a itself gets only incremented once. assert( s[1] == 'a'). It can be applied to a member variable to cancel out the const declaration. // some more functions modifying s and setting valid to false }.. but the referenced character  array could be changed. size_t string::length() const { if ( ! valid) { l = strlen(s). // error. Two examples: the array index  operator to a single character element in the array. The key is  that the class never exposes.   class string { char* s. i. Note that we want to keep the  full functionality for the non-const case as well. mutable bool valid. i. and pointers. We can forbid the second increment explicitly by making the  return type. public: string() : s(0).C)   class string { char* s. } . bool valid. we can use it safely without breaking const correctness. // German string s. valid(true) {} size_t length() const.e. none of its non-static member variables can change  its value. violates bitwise constness checked by the compiler! } return l. public: string() : s(0). the compiler guarantees bitwise constness. The second problem of bitwise constness occurs with pointers and references. (see also string. returns. a++++. violates bitwise constness checked by the compiler! valid = true.   int main() { char buf[6] = "Hallo". and a member function returning the raw character pointer. a non-const pointer or non-const reference to the array elements. // some more functions modifying s and setting valid to false }.} const char& operator[]( int idx) const { return s[idx]. once for the const case. Given this string class.. class string { char* s.} char* & get_pointer() { valid = false. // the post increment operator Const Correctness and C++ Classes Given an object of a class is declared const. // Now it's in English assert( s[1] == 'e'). // const. valid(true) {} size_t length() const. l(0). does not change string conceptually . Two reasons: internal..  Bitwise constness and conceptual constness are not the same. However. This should be considered for all similar temporary return types. } const char* get_pointer() const { return s. Bitwise constness of a pointer says that the pointer cannot  change. }. return s. but the referenced value can change. // error. s. // get a const reference to the same string: const string& r = s. mutable bool valid.htm 1/13/2010 . const. Fine. There might be internal variables that can change but that cannot be observed. the referenced  value should be considered constant as well. // const. the type of the temporary object. not observable variables. But it probably does not what one would expect.

Proxy& operator= ( char c). int main() { string s = "Hello!". Meyers97]. manipulate str if necessary Proxy& operator= ( const Proxy& rhs). int dim2). such as in the first example  above.. or the assignment of a character to the proxy. } However.. }. // r-value uses (str does not change) (automatic conversion to char) operator char() const.  file://C:\Users\rmanoj\Desktop\Untitled. // . }. Proxy operator[]. // . class Array2D { public: Array2D( int dim1.. int dim2).. Array1D& operator[](int i). r-value assert( r[1] == 'e'). The problem appears with string classes and their operator[] assuming the string class uses reference counting with 'copy-on-write' strategy. s[5] = 'a'. We can defer this decision by introducing a proxy for  the return type of operator[]:   struct string { const char& operator[] const. int i = a[2][8]. // . struct string { const char& operator[](size_t index) const. it represents a one- dimensional array. The intermediate class Array1D is called proxy class. which corresponds to an l-value assignment. Of course. } Note that the client code does not change.. // . but in this application we surely do not want to copy the elements to actually create a one-dimensional array.. The  proxy contains a reference to the string it belongs to:   class Proxy { string& str. such as in the second example above. in a program we would like use the array similar to the builtin (static) two-dimensional arrays and access an element as follows:   int main() { Array2D a(5. Clients can pretend that operator[] returns a char in most cases. index(i) {} // l-value uses..   class Array1D { public: Array1D( int dim). }. the non-const index operator will be used for both uses of the operator[]. char c = s[3]. // .10). we can implement operator[] to return conceptually a one-dimensional array. int index. // r[1] = 'a'. int operator[](int i).. // .Page 33 of 72 // r. } Since s is not declared constant. Instead. which does not change the string. Conceptually.. Since the string could be  modified through this operator and it cannot see that the result of operator[] appears on the right side of the assignment in the first  example. does not work with const char & } Proxy Classes A dynamic two-dimensional array of integers could be declared in C++ as follows:   class Array2D { public: Array2D( int dim1.. the string class must make the conservative assumption that it will be changed.  Another typical example for proxy classes is to distinguish between read and write access. } The proxy distinguishes two cases: automatic conversion to a character r-value. where  we can apply operator[] again to retrieve the element. }. The proxy  class will just behave as if it is an one-dimensional array and internally it will use a pointer to the two-dimensional array to implement its  operations. // does not work with const char *. // no. int i) : str(s).. public: Proxy( string& s..htm 1/13/2010 . there is no operator[][] in C++.get_pointer() = "Salute". char& operator[](size_t index). also known as surrogate [Item 30.

 but has some additional ``smart'' functionality. In a copy operation. (For more information on smart pointers. we should also consider the distinction between constant and non-constant member functions. or the operators +=. But in case we would deal with a class with member functions instead of a builtin type..)   Smart pointers usually point to an object in dynamic memory and own it. i.Page 34 of 72 Limitations pop up for other l-value uses of the proxy than assignment. back_insert_iterator Smart Pointers A smart pointer is an object that behaves much like a pointer. -=. } void sink( auto_ptr<int> pt) {} file://C:\Users\rmanoj\Desktop\Untitled.   Perhaps the most fundamental difference between various smart pointers is how they deal with copying. which is not acceptable. The compiler cannot compose automatic conversions. they are responsible of destroying and deleting the object when appropriate. some expressions that work without proxies do not work with proxies.htm 1/13/2010 . Thus. not always obvious places such as passing an argument by value. This is the biggest deficiency of built-in pointers: void f() { int* p = new int(42). 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 context. However.   The auto_ptr solves the copying problem with destructive copy (or move) semantics. Smart pointer classes differ  greatly in what additional functionality they provide and how closely they mimic built-in pointers. all these operators can be implemented to behave correctly. They have to be implemented in the Proxy as well. auto_ptr<int> source() { return auto_ptr<int>( new int(42)). Assigning a value to the proxy writes it to the output iterator. because  copying occurs in many. *= etc. Consider: void f() { SmartPtr p(new int(42)). // do something delete p. is a container of pointers (see below). } The most obvious implementation would lead to deleting the same object twice. } This looks fine at first. appends it to the underlying container in this case.e. the new pointer obtains the ownership and the old pointer becomes a null pointer. but it can lead to a memory leak if the ``do something'' part of the code throws an exception (see Exception Safety) or  contains something like:   if (done) return. This is a big problem. The is an output iterator and we use a proxy as a return type of operator*. auto_ptr. SmartPtr q = p. It is meant as a replacement for built-in pointers for:   holding dynamically allocated objects   passing ownership of dynamically allocated objects into and out of functions   It is not meant for containers of pointers. For example. where the failure of a built-in pointer to delete its pointee is problematic. #include <memory> using namespace std. The following three sections display four different approaches to copying smart pointers:   destructive copy (move)   no copying allowed   (polymorphic) deep copy   reference counting   Smart Pointers: std::auto_ptr<T> The standard library provides one smart pointer template. and inserting to a standard  container. Chapter 7] or Boost smart pointer documentation. We have already seen the use of a proxy in the STL when implementing back_insert_iterator (see this Section). see  [Alexandrescu01. Another limitation is that proxies rely on an automatic conversion and the compiler can only use one automatic conversion to resolve expressions. returning by value. Note that in this example the proxy class is actually the back_insert_iterator itself (for no good reason except to write less code). taking the address of the result of operator[]. that is.

 Thus. } // still make it behave like a pointer Shape& operator*() { return *shape.. public: DeepPtr(T* p) : ptr(p) {} DeepPtr(const DeepPtr& other) : ptr(other->clone()) {} ~DeepPtr() { delete T. // but this is not ++*pt. so is the object it points to. but most likely it does not work as expected. storing objects of type Shape wouldn't allow us to store objects of type Circle. This value could be declared constant as well. std::sort( vec. pt = source(). Container classes in the STL store objects by value.end()). let us take a look at a smart pointer that is. We distinguish between two such cases:   Invisible sharing.htm 1/13/2010 .shape). there is no sharing.Page 35 of 72 int main() { // these are legal and safe expressions with auto_ptr sink( source()).)   One solution is to have just one owning pointer per objects. // error: dereference of a null pointer } How about the following two lines?   std::vector< auto_ptr<int> > vec. we will use a smart pointer. (Note that reference counting is not suitable for cyclic data structures such as graphs.  How about the following line? const auto_ptr<int> ptr( new int(42)). sometimes we want multiple pointers to share the same object. we can store pointers to Shape in the container. we use deep-copy semantics: when a pointer is copied. } }. no copy and deep copy. It depends. vec. other. }  void swap(DeepPtr& other) { std::swap(ptr. } ShapePtr& operator= (const ShapePtr& rhs) { ShapePtr tmp(rhs).ptr). } T& operator*() const { return *ptr.begin().g.C for the full example. the virtual clone() function is provided for performing a polymorphic copy. they  would be sliced to type Shape when we try to store them in the container. other. The reason is that the unusual copy semantics of auto_ptr<T> (modifies the righthand side) does not comply with the requirements for the template parameters of STL container classes and algorithms. maintain the invariant that there is exactly one pointer  pointing to each object. the value it refers to can be changed. Koenig96]. We use a small hierarchy of shapes as example. class ShapePtr { Shape* shape. sink( pt). With the lack of ownership of plain built-in pointers. public: ShapePtr(Shape* s) : shape(s) {} ShapePtr(const ShapePtr& other) : shape(other->clone()) {} void swap(ShapePtr& other) { std::swap(shape. template class DeepPtr { T* ptr. struct Circle : public Shape { virtual Circle* clone(). return *this.  Instead. struct Square : public Shape { virtual Square* clone() = 0. This is useful as an optimization to reduce the cost of copying large  objects such as strings. However. It cannot even be copied to another const auto_ptr<int>. (See auto_ptr. virtual ~Shape() {} }. return  *this. e. destructive copy. } }. From user's point of view. for example. swap(tmp). That is a truly const pointer. this requires a guarantee that the owning pointer lives longer than all  file://C:\Users\rmanoj\Desktop\Untitled.)  Smart Pointers: Polymorphic Deep Copy As auto_ptr is not suitable for containers. } DeepPtr& operator= (const DeepPtr& rhs) { DeepPtr tmp(rhs). auto_ptr<int> pt2 = pt. However. Here. This smart pointer helps in managing objects of a class  hierarchy in a container class [Chapter 5. } T* operator->() const { return ptr. auto_ptr<int> pt = source(). }. For copying.  because cycles would never get deleted. }. swap(tmp).  --> size=2 width="100%" align=center>   Smart Pointers: Reference Counting The above solutions to the copying problem. a set of objects stored in multiple  associative containers with different search keys.   struct Shape { virtual Shape* clone() = 0. However.   Visible sharing. } Shape* operator->() { return shape. This represents another approach to copying: no copying allowed. Used when an object needs to be accessed throug multiple routes. const auto_ptr<const int>.

by deriving from the class Rep: struct Rep { int count. swap(tmp). it often makes sense to use handles instead of pointers. (see also handle_ref. assert( b. In the two non-intrusive variants illustrated below. Allowing modifications is a problem when the sharing in invisible. Note that the handle is a class template parameterized by the representation it refers to. For example.  struct Integer_rep : public Rep { int i. A handle does not behave like a pointer but like the object it points to. The following example shows how to use these classes to implement a class for integers that uses reference counting (of course. because user does not expect  that the modification of one affects another.htm 1/13/2010 . REP* ptr. Instead of dereference operations it has all the public member functions of the pointed-to class. return *this. for example.value() == 5). // Precondition: REP must have a member 'int count'. where the reference count is a private variable to protect the reference counting scheme from user code)  int main() { Integer a(5). The pointed-to object (representation) contains the data members and the reference counter. and handle_ref_extended.ptr) { ++ptr->count.Page 36 of 72 the non-owning ones. A solution is a strategy called copy-on-write. } }. other. the penalty is either large pointer or a more costly dereference operations. The object is deleted when the counter drops to zero.ptr). } void swap(Handle& other) { std::swap(ptr. the representation has been constant. If we are in a position to design the class from scratch. extra memory allocation is needed for the counter. Boost shared_ptr uses the design on the right. The common functionality of handles and representations can be factored out into common base classes. } Handle& operator= (const Handle& other) { Handle tmp(other).C for a more  protected example. The representation is supposed to contain a count variable. A safer and more user-friendly alternative is reference counting. the standard string class is a handle. The pointed-to object has an associated counter  keeping track of the number of pointers referring to it.   The intrusive smart pointer requires modifying the pointed-to class to provide the counter. Rep() : count( 1) {} }. template < class REP > class Handle { protected: // Invariant: ptr is always != 0. reference  counting does not pay off for plain integers). a = b. Before modifying the representation. Furthermore. class Integer : public Handle<Integer_rep> { public: Integer() {} Integer( int i) : Handle<Integer_rep>(i) {} int value() const { return ptr->i.   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). Integer b(a). } }. the count is  file://C:\Users\rmanoj\Desktop\Untitled. } So far. Integer_rep() {} Integer_rep( int j) : i(j) {} }. public: Handle() : ptr( new REP) {} Handle( const REP& rep) : ptr( new REP(rep)) {} Handle( const Handle& x) : ptr(x. The non-intrusive variants can be used with any types of objects (just as ordinary pointers) but they cannot be implemented without some overhead. a handle can be used just as a normal type. } ~Handle() { if ( --ptr->count == 0) delete ptr. In a program.C for the full example. but might not have any of the public member functions.

perform cleanup.. } // point of error // leak! // never executed void safe_the_hard_way() { int* a = new int[100].   It is difficult or impossible to make a function exception-safe and -neutral if it calls a function that is not exception-safe and -neutral. and re-throw the (same or a different) exception. Note: ``a resource'' (see  next guideline).htm 1/13/2010 . including libraries. Then we need to  transfer the execution from the point of error to the point of recovery. a member function may not leave an  object in a state.   Exceptions are a mechanism in C++ to handle this kind of error situations. That is. // re-throw the original exception } delete[] a.) { // catch anything delete[] a. An exception is an object (of an arbitrary type) that is thrown at the point of error and caught at the point of recovery.  Exception Safety When an error occurs in a program. closing files.   2. error(). The two points may be separated by a long chain of function calls. it often makes sense to write a separate class just for managing a resource.   file://C:\Users\rmanoj\Desktop\Untitled. For example. If the  standard library facilities are not enough. throw. Functions on the error path may also catch the exception. It may carry information about the error. we want to terminate a low level operation but recover and continue at a higher level. There are three levels of exception safety:   1. Basic guarantee: No resource leaks. there are two possible responses:   terminate   recover and continue   Often we want to do both. } catch (. } } A function is exception-safe if it performs proper cleanup for any possible exception.. This technique is frequently referred to as ``resource acquisition is initialization''. etc. As demonstrated by the  safe_the_easy_way() function above.   Guideline: Resource Acquisition Is Initialization The preferred way to achieve exception safety is to do resource acquisition (such as memory allocation) in a constructor and let the destructor  take care of cleanup.Page 37 of 72 checked. Some cleanup may be necessary on the error path: destroying objects. where the destructor might not be able the free all resources. // safe now try { unsafe(). safe_the_hard_way(). Nothrow guarantee: Never emits an exception. a new copy will be created in which the modification takes place. } void unsafe() { int* a = new int[100]. } catch (std::exception& e) { // does not catch "error" cout << e. Templates are particularly vulnerable since  they know little about the exceptions the template parameters might throw. } void safe_the_easy_way() { vector<int> v(100). void error() { throw "error".   3. All automatic (non-static) local objects on the error path are destroyed by calling their destructor. The functions on the error path may come from different sources.  cancelling partial changes.. delete[] a.what() << endl.  which we call the error path. freeing dynamic memory. } // safe: destructor cleans up // safe: destructor cleans up int main() { try { safe_the_easy_way(). For this reason library code should be exception-safe and -neutral. // but would catch std::bad_alloc } catch (const char* s) { // point of recovery cout << s << endl. The function either completes the operation it was performing or leaves the program state  unchanged as if it was never called. If it is greater than 1. The guarantee includes indirect resource leaks. Strong guarantee: Commit-or-rollback. auto_ptr<int> p(new int(42)). and exception-neutral if it propagates all exceptions to the  caller. standard library provides convenient facilities such as containers and auto_ptr for this. One weak  link can destroy the guarantee.

Thus exception safety should be considered early in the design process. in the Person class above. This is often undesirable. For this reason. For example. no. Guideline: Separate Responsibilities A function or a class with multiple responsibilities is harder to make exception-safe than one with a single responsibility. Is it exception-safe? int counter = 0. } // safe now ~Person() { } // bonus: empty destructor }. the function does not complete its job (which includes returning a string). the destructor of a container (even a basic array) calls the destructor of all its elements. ++counter. } At the basic level. which means that the destructor is never called. } Still not strongly exception-safe. pp.. the evaluation order could be new int(42). (See [Sutter99. string f(const Person& x) { string tmp = x. We can achieve strong guarantee by  returning a pointer to string (copying a pointer cannot throw). yes. return tmp. If x. auto_ptr<string> f(const Person& x) { auto_ptr<string> tmp(new string(x. return x. ++counter. string f(const Person& x) { ++counter.Page 38 of 72 Writing exception-safe constructors and destructors requires some extra care. g()).   Here is a function with two responsibilities: incrementing a counter and returning a value. At the strong level. A better alternative is to use auto_ptr:   int counter = 0. the object remains partially constructed. Returning involves a string copy construction.   void f(auto_ptr<int>(new int(42)). moving the responsibility for the image memory to  auto_ptr was the key.  but the counter has been incremented. 25-54]. which might throw. Moving each  responsibility to a different entity helps. The order of evaluation of function arguments is not specified by the standard.htm 1/13/2010 . } ~Person() { delete image. the STL stack (adapter) splits the two responsibilities between two function: top() returns the top  element and pop() removes it. For example. public: Person(const Image& i) : img(new Image(i)) { init().. The following is better. In the above line.name(). class Person { Image* img. auto_ptr<int>(. } Here we achieved exception-safety by changing the return value of the function. Thus the simple cure here is to use auto_ptr:   class Person { auto_ptr<Image> img. has a similar two-responsibilities problem. Recall that a destructor may be called during exception handling. This could lead to memory leak if g() throws. void init().name(). return tmp. Guideline: Separate Throwing Code form Critical Code Consider the following line.)   Another point illustrated by these examples is that exception safety is not just a matter of implementation details: it can affect the interface. the resulting behavior is undefined. void init().   file://C:\Users\rmanoj\Desktop\Untitled. A destructor should never emit an exception. If one of the element destructors throws.name() throws. but changing the return type to auto_ptr is  not really acceptable. Furthermore.name()).). } }. a stack operation pop () that both removes the top element and returns it. A desctructor throwing in this situation terminates the program immediately. What about this?   int counter = 0.. public: Person(const Image& i) : img(new Image(i)) { init(). } catch (. if a destructor has to do something that might throw. Therefore.. // leaks if init() throws Fully constructed subobjects (members and bases) are destroyed.  g().) { } // catch everything without re-throwing } If a constructor throws. it should catch the exception: X::~X() { try { write_log("Destroying X").

 templates cannot be expected to know what  exceptions their template parameter types might throw. an  integer. // copy constructor void swap (const A& other) throw(). in each item. they are less useful: Violations are discovered at run-time.   Associate fields dynamically at runtime with a hash array: This is the more flexible variant of the previous item. a C library for graph algorithms in C by D. we want to associate an additional data field with each item. not at compile-time as with const specification. where an exception could be thrown. A good example is the canonical copy assignment operator implemented in terms of a copy constructor and a swap: struct A { // . the adaptor (a.  Additional attributes can be just added to the dictionary.g. The user has to give the right arguments (at compile  time) dependent on the algorithms the user wants to use. The algorithms have to  cooperate on the use of these fields. or they could implement an alternative method to create additional bits. Changes in the new data structure are also reflected in the underlying old data structure. However. This is a limited solution in case the enumeration is easy and  the data structure does not change. The guarantee relies on the no-throw guarantee of swap.   Let each node manage additional fields dynamically: Each node contains a dictionary (hash array) that contains name-value pairs. Algorithms could state the number of needed bits as  precondition.. However. see below. implementing an adaptor  just to add a boolean for a depth first search is a bit of an overkill and can be quite some effort to support all operations on a graph  file://C:\Users\rmanoj\Desktop\Untitled.   The no-throw guarantee of swap was formalized in the exception specification throw(). This solution is as flexible and even easier to use than the hash maps in the previous item.   Templates: Instead of hardcoding all fields. }..k. operation has no effect swap(tmp). To access the specialized item type a dynamic cast is usually needed. hashing is quite costly if it is only needed to associate a boolean with each  node. // swap *this with other A& operator= (const A& other) { // copy assignment A tmp(other).. vertices or other uses.. the Stanford GraphBase. the operation exits without any  permanent effect. we have the strong guarantee.   Associating Data to Items in a Data Structure Given a data structure with items. In particular.a. A  typical application would be a boolean field for a graph traversal algorithm. Instead of a simple  array a hash array or an associative map is used to create the additional fields. for example.   Derivation: Derive a more specialized item type and program the data structure and the algorithm in the object-oriented style such that  they can cope with derived classes. Knuth.   Provide some general purpose fields: For example. Exception specifications should be used carefully. is written using the old data structure  underneath. The throwing code could be performed on automatic local objects that get destroyed if an exception is thrown. This application also highlights a possible  dynamic nature of this associated fields. Let the data structure manage who uses the different bits in the bitfield dynamically. and they cause an immediate termination of the  program. The benefit is the saved space if some algorithms are not used. preferably doing the throwing code first. a graph with nodes and edges. If this happens. swapping pointers not pointees) and can be implemented using only copy operations on built-in types. } // . an exception specification lists the exception that might be thrown. However. void f(p.htm 1/13/2010 . g()). depth first search. e. New data can be integrated in the  adaptor. In the general form. Provide a bitfield. It can hide the need for specific fields from the user. make them a template parameter. Thus. typed pointers to edges. The only place in operator=. More generally. // if this throws. After the  algorithm finishes it returns the bitmask to the data structure and the bit is free to be used by some other algorithm.Page 39 of 72 auto_ptr<int> (new int(42)). it has to  be considered what should happen if the data structure runs out of bitmasks. has six  utility fields and macros to access these fields as integer values. effectively canceling the operation. Several solutions are possible:  Provide all possible fields: Make the field a requirement for the data structure and implement all fields that come up in the library. E. only a particular sub-algorithm might require them. Usually swap performs a  bitwise swap (for example. it is useful to separate the code that might throw an exception from the code that performs critical operations.   There could be a run-time overhead even if no exception is thrown. A(const A& other)..   Adaptor pattern: With the adaptor pattern.   Manage some general purpose fields dynamically: This solution is specifically useful for boolean fields.   Enumerate nodes and associate fields dynamically at runtime with an array: An array is used to create within an algorithm additional  fields that are associated with the enumerated items of the container class. This is the most flexible and easy-to-use solution and it  can hide the need for specific fields from the user. While they look similar to const specifications. which  cannot throw. is the copy construction. a new data structure. wrapper). This solution can introduce a noticable runtime overhead if rather small functions have to be made  virtual.   Note that a lack of formal exception specification in the code is not an excuse for omitting (a more informal) exception specification in the  documentation. An algorithm can allocate  from the data structure a bitmask and it can use this bitmask to manipulate the corresponding bit in the bitfield of the items.   Determining what exceptions could possibly be thrown is not easy. // can not throw return *this.   Addition of a new exception could cause a lot of changes if exception specifications are used frequently.

 Even these are not provided for all  property maps. Since we intend to call the equality operator of the derived class. arrays. value_type oldname = get(name. } }.C)   template <class T> class Inequality { public: bool operator != (const T& t) const { return ! (static_cast<const T&>(*this) == t). The same technique can be used to implement a base class for iterators that contains all those small member functions that are defined in  terms of a much smaller set of member functions. put(). a boolean implemented as a bit in an integer cannot be a model of LvaluePropertyMap. which is a safe down-cast in this case. class A { public: bool operator == (const A& a) const.. If we derive an iterator of the forward  category.  Barton97]. // restore old name } The example illustrates the three operations on property maps: get(). (see also  barton_nackman. The inequality comparison operator is implemented in terms of the equality operator. Here. bool operator != (const A& a) const { return ! (*this == a). }.C)  There is only one pitfall with this solution: name lookup rules for overloaded functions in class hierarchies. For  example:   template <class Vertex. The pre. A generic programming approach is  taken by the Boost Graph Library [Siek01]. provides an equality and an inequality comparison operator. // assign new name value_type& name_of_v = name[v].   class A : public Inequality<A> { public: bool operator == (const A& a) const. Even better. the solution is to inject the derived  class as a template argument into the base class. Of course.   LEDA graphs offer several of these possibilities: general purpose fields. However. }.  (see also Iterator_base. Wouldn't it be nice to factor out this generic  implementation into a base class and share it with all classes of this kind? The problem is a cyclic type dependency. But the base class needs to know the derived class as well. The additional pointers for linking the adaptor with the adaptee costs also some additional space. newname).  We start with a simple class that. // get old name value_type newname = "New". since the base class is a template class. and hash arrays. } // . among other operations. The name lookup stops as soon as the function name has been found. [Coplien95] documents some more occurrences of these "curiously recurring template patterns". we have to use a type cast. class NameMap> void foo(Vertex v. v. and it does not search for more overloaded function in base classes.htm 1/13/2010 . There are four property map categories (similar to iterator categories):   Concept   Refinement of   Syntactic requirements  get()   put()   ReadablePropertyMap   CopyConstructible   WritablePropertyMap  CopyConstructible  ReadWritePropertyMap  ReadablePropertyMap. templates. NameMap name) { typedef typename boost::property_traits<NameMap>::value_type value_type. where algorithms are implemented using property maps that hide the actual mechnism. put(name. the extra random access operators in the base class are just ignored and don't cause error messages as long as they are not used. // check the change name_of_v = oldname. which is also constoverloaded): template < class Derived> class Iterator_base { file://C:\Users\rmanoj\Desktop\Untitled.Page 40 of 72 class. the (then)  derived class A needs to know the base class. since otherwise it cannot call the  correct equality operator.. and operator[](). WritablePropertyMap  -   LvaluePropertyMap  ReadWritePropertyMap   operator[]()   For example.and postincrement operator are an example (note also the use of a private member function to encapsulate the type cast. we can make use of the "lazy" implicit  instantiation and implement the most general base class for iterators of the random access category.h and Iterator_base. and the derived class shows up in the operators type signature as well. because it is not possible to have a reference to it. v). assert(name_of_v == newname). Solving Mutual Dependencies with the Barton-Nackman Trick The following C++ technique is usually referred to as the Barton-Nackman trick since they have introduced it in their book [page 352.

  // forward declaration template <class A. class Some_iterator : public Iterator_base< Some_iterator> { // . i++. edge.. an edge knows its two incident nodes. }. }. The compiler does not find the correct post-increment  operator in the base class and gives an error message.double> edge. we can use partial specialization to  write a specializes version. int main() { Node<int... B aux. template <class A.edge = &edge. } // node with One disadvantage of this solution is that the auxiliary data is always there and consumes space. class B> struct Edge { Node<A. A solution could look like this (see also graph. edge. It would be nice to  specify it as void in this case. class B> struct Edge. maybe more than one edge .Page 41 of 72 Derived& derived() { return static_cast<Derived&>(*this). A aux. Only overloaded instances of the  function in this class and global functions are now used to resolve the function call.B> { Edge<void. to get rid of the reserved auxiliary data if we specify it to be void.aux = 5. public: Self& operator++(). a local variable void aux is not going to work in C++. and the edge has to know the type of the node. A node knows incoming and outgoing edges.B> * edge.. }. Edge<int.6. In the spirit of generic programming we  file://C:\Users\rmanoj\Desktop\Untitled. }.htm 1/13/2010 .B> * node. } The post-increment call causes the compiler to look for the function name operator++ (without type signature for the arguments) in the class  hierarchy. If we want to parameterize nodes and edges with a template parameter for some additional auxiliary data. template <class A..C for an example with an additional graph class). } // . // . } public: const Derived operator++(int) { Derived tmp = derived().. However.. struct Node { Edge * edge.node = &node. even if it is not needed. both also have to know the template  parameter of each other. node.  Solving Mutual Dependencies between Class Templates A graph consists of nodes and edges. // .. int main() { Some_iterator i.double> node. In this solution the node type and the edge type are tightly coupled together (as in the C solution). Implementing this  cyclic type dependencies between node and edge type would use a forward declaration in C:   struct Edge. }.aux = 6. ++ derived().   template <class B> struct Node<void. here for the node. It finds the pre-increment operator in Some_iterator and stops with the function lookup. node. But  since the node has to know the type of the edge. class B> struct Node { Edge<A. Node * dest.... we can follow the same idea.B> * edge. However. return tmp.h. The solution is a workaround.. struct Edge { Node * source. } const Derived& derived() const { return static_cast<const Derived&>(*this). }. }. see Iterator_base. implement all overloaded functions in the base class and  give the involved functions in the derived class a new name.

edge = &edge. edge. } It is important to understand that these cyclic definitions work -- as for the C example -- because we can make use of a declared type to define  pointers and references to this type before this type is defined itself.  template <class Graph> struct Colored_node : public Node<Graph> { int color. }. // . given a model for a node and a model for an edge they should work together in a graph class. but declaring the node constant...  Const correctness is an issue for a graph data structure.C for a more extensive example).Page 42 of 72 might want to decouple them. G::Edge edge. G::Node node. Edge> G. Meyers96]).  We might specify a concept for a node and a concept for an edge.node = &node. maybe some more edges . There is a graph class that takes two parameters. Edge* edge. declaring a node constant. Node* node.. The missing type information for a node as well as for the edge is provided with a template parameter Graph. }. template <class G> class T_Edge> struct Graph { typedef Graph< T_Node. } To illustrate the flexibility in this design. We use a similar mind-twister as for the Barton-Nackman trick in the previous section. and therefore the graph class passes itself as template argument to both types. typedef T_Edge<Self> Edge. node. and graph3.C the adjacency list Edge_list edges is of particular interest. edge. Edge> G. G::Node node. template < template <class G> class T_Node. }. Imagine to exchange a vertex type Node with another vertex type Node' as in the following figure. all access to the contents of this list has to return const_iterator's to edges. node. the graph knows the node and the edge class that are supposed to work together. For example. node. For the more extensive example graph3. int main() { typedef Graph< Node. G::Edge edge.. For example. we implement a new node class by deriving from the old one and add a member variable for color (see  also graph2. 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). Making a Function Virtual for Two Arguments Imagine a game in space with ships.edge = &edge.C for this example. T_Edge> Self. }. template <class Graph> struct Edge { typedef typename Graph::Node Node... The graph class uses itself as parameter to the node template and the class template to define the local types for node and edge.node = &node. it should not be possible to traverse the graph in any fashion and to reach a mutable node or edge. template <class Graph> struct Node { typedef typename Graph::Edge Edge. typedef T_Node<Self> Node. we cannot change the pointer member Edge * edge of  the node class to a value Edge edge. Collisions are handled as follows:  file://C:\Users\rmanoj\Desktop\Untitled. base stations and asteroids (see also [Item 31. To summarize. int main() { typedef Graph< Colored_node. Double Dispatch. Now.htm 1/13/2010 .color = 3. This list contains iterators to edges.

 Switch/case statements tend to be unmaintainable and are not extendible. We change the problem slightly and think of two different types that should interact with each other. For unknown types. struct Ship : public Game_object { virtual void collision( Game_object* other) { // this (of type Ship) collides here with other. }. 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. else ships get  destroys asteroid if it small. } virtual void collision2( Ship* other) { // Ship collides with ship. and a set of algorithm that work on the tree nodes. struct Ship : public Game_object { virtual void collision( Game_object* other) { // this (of type Ship) collides here with other } }. else station get  asteroids break into smaller pieces   destroyed   destroyed   Assume all three objects are derived from a single abstract base class. Meyers96] is rather complicated and uses STL maps. and give the second call with reversed parameters a different name (see double_dispatch_ext. But. else ships get  destroyed   destroys asteroid if it small. else damage   damage proportional to speed   Asteroid   destroys asteroid if it small. This can be solved by breaking the symmetry. struct Station : public Game_object. see the Section Visitor Pattern. // similar struct Asteroid : public Game_object. virtual void collision2( Ship* other) = 0. file://C:\Users\rmanoj\Desktop\Untitled. it calls the collision function again. virtual void collision2( Asteroid* other) = 0. to make the family of algorithms extendible. call second dispatch other->collision2( this). An example would be a tree hierarchy composed of different node types. struct Game_object { virtual void collision( Game_object* other) = 0. }. this solution does not solve the problem of  extendibility. this is the solution. Each collision knows the correct type of its this pointer. We start with defining a virtual collision handling function that takes the second game object as a parameter. Adding another game object. but with the reversed order of arguments. which resolves the first dispatch along the type of the first argument.C for this example). virtual ~Game_object() {} }. A possible object-oriented  solution uses a set of virtual member functions to dipatch along the second argument (see double_dispatch_static. would require to add a virtual member function to each class handling stars now. the second dispatch along the type of the second argument remains unresolved yet. we can observe that the collision handling is symmetric with respect to its two arguments. But the other  pointer is still of the abstract base class type. Among the classes has to exist an order. for example stars. }. 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. virtual ~Game_object() {} }. }. We can make this double dispatch problem extendible for either one of these types. virtual void collision2( Station* other) { // Ship collides with Station. a new class has to implement the collision with all old classes). virtual void collision2( Asteroid* other) { // Ship collides with Asteroid. Or we can use the visotor pattern. the solution described at the end of [Item 31. A class has to implement the collision for all classes that are smaller in this order (in other words. the  object-oriented way doesn't like this. If the class hierarchy is known and does not change in the future. virtual void collision2( Station* other) = 0. But we probably do not want to implement each function twice. We could  use a switch/case statement to distinguish its actual type (using runtime type information (RTTI) with the typeid function).  struct Game_object { virtual void collision( Game_object* other) = 0. We can just encode the algorithms as member functions in the different tree node types. There is the danger of missing to test for one class.  Furthermore. // similar struct Asteroid : public Game_object {}. However. in which case this solution produces an infinite loop (until the stack oveflows). Now it is easy to add a new tree node type.Page 43 of 72 Ship   Ship   damage proportional to speed   Station   docking if slow.htm 1/13/2010 .C for this example). Since we don't have the support of the compiler here (convinient index generation into this table etc. 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.). else station get  destroyed   Station   docking if slow. struct Station : public Game_object {}. else damage   Asteroid  destroys asteroid if it small.

class T2> void collision( T1& o1. Station& void collision( Ship& o1. and  replacing exact arithmetic by imprecise built-in floating-point arithmetic does not work in general. Computer graphics and virtual reality. computer aided design and manufacturing. can help understanding the practicability of  algorithms. Only recently have implementations of algorithms been rediscovered as an active topic in software engineering. Others require unbounded degree or algebraic roots.   Theoretical papers assume exact arithmetic with real numbers. such as fatness. Ship& void collision( Ship& o1. robotics. As a result the program may crash. Asteroid& o2). polygons. and key problems and problem classes have emerged. the report now demanded to address also the applicability in practice. but for other problems the special case treatment distracts from the solution of the general problem and it can amount to  a considerable fraction of the coding effort. and circuit design are well-known examples. for example with the generic programming paradigm [Musser89]. the field has developed a rich body of solutions to a  huge variety of geometric problems including intersection problems. and the worst-case usually depends on a general  input model that may be unrealistic. Usually the output is only an approximation of the exact solution. either to convert the input into the format required for the arithmetic (rounding floating point to integer). the main focus in object-oriented design is on data abstraction. solid modeling. 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. Typically. and polyhedra.   The requirements on the arithmetic vary with the algorithm. T2& o2) { collision( o2. theoretical papers exclude degenerate configurations in the input. CGAL. A new class has to implement all possible combinations with old classes. Geometric algorithms in particular  are sensitive to rounding errors since numerical data and control flow decisions have usually a strong interrelation. For some problems. 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. Since templates work at compile time we have to know the actual types of colliding objects and we cannot work with base class pointers.. o2).Page 44 of 72 Templates could be used to solve the double dispatch problem conveniently if we change the setting slightly. Simple examples of configurations considered as  degenerate are duplicate points in a point set or three lines intersecting in one point. lines.   Furthermore. may run into an  infinite loop. 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: Algorithms in geometry are among the most advanced algorithms in algorithm design and they make frequent use of complicated data  structures. Over the past two decades.  Geometric algorithms arise in various areas of computer science. shape reconstruction. the constant hidden in  the asymptotic analysis can easily outweigh asymptotic factors. the specialized functions implement the actual collision handling. Another approach is to redesign the algorithm to cope with inexact arithmetic. } void collision( Ship& o1. visibility problems. i. Some algorithms require only sign computations of polynomial expressions of bounded degree in the input variables. such as log-factors. 6. Computations with real numbers are assumed to be in constant time.htm 1/13/2010 .e. o2). Often. Recommendation number one on the list of four was ``production and distribution of usable (and useful) geometric codes''. Various packages for exact arithmetic are available for different needs. As a common prerequisite for exact arithmetic the input is rounded. The generic template implements the symmetry. Station& void collision( Station& o1. The numerical  problems may destroy the geometric consistency that an algorithm may rely on.   In theory. its reuse and the design of large-scale systems. they have zero probability if the input set is randomly file://C:\Users\rmanoj\Desktop\Untitled. Although crediting the remarkable success of the field in theory. template < class T1. The asymptotic worst-case complexity analysis does not match the practical need for two reasons. the Computational Geometry Algorithms Library Introduction Computational geometry is the sub-area of algorithm design that deals with the design and analysis of algorithms for geometric problems  involving objects like points. A number of fundamental  techniques have been designed. Asteroid& void collision( Station& o1. computer vision. This solution is extendible. and would involve the treatment of special cases in the algorithm. o1). and proximity problems. relationships among data. In practice. o2). o2). To a large extent the theory has been developed with asymptotic worst-case complexity analysis and under the assumption of the real RAM model. geographical information systems. Application Challenges to Computational Geometry. these degeneracies are specific to the algorithm and  the problem. In 1996 the Computational Geometry Impact Task Force published a task force report. o2). or to lessen the precision requirements on the arithmetic. software engineering has ignored the difficulties in algorithm engineering for quite a while. For example. or -- perhaps worst of all -- may produce unpredictable erroneous output. Asteroid& void collision( Asteroid& o1. The correctness proofs of the algorithms rely on exact computation. data encapsulation. More realistic input models. it is not difficult to handle the  degeneracies. molecular modeling.

html) developed at ETH Zurich. degenerate input occurs frequently.ethz. and software libraries.de/LEDA/). plus 50.mpi-sb. The library is being developed by several universities and research institutes in Europe and Israel. especially flexibility and efficient robust computation. For instance. Generic programming with templates in C++ also provides us with the help of strong type checking at compile time. The birth of the CGAL-library dates back to a meeting in Utrecht in January 1995. hard to extend. flexibility.000 lines of C++ source code for the library. Compared to the graphics gems. which has been developed at Max-Planck-Institut für Informatik.4 of May 2002 consists of approximately 290. An overview on the state of the art of computational geometry software before CGAL including many  references is given in [Amenta97]. Moreover. Another example is GeomLib. General approaches in handling degeneracies are symbolic perturbation or randomized perturbation with performance and correctness guarantee. One aspect of flexibility is that CGAL algorithms can be easily adapted to work on data types in applications that already exist. and hard to reuse in other projects. the five authors of [Fabri99] started developing the kernel. and to reject the object-oriented paradigm in C++ (as well as in Java). Examples are the precursors of CGAL developed by members of the CGAL consortium. efficiency. and the geometric part of LEDA (http://www.mpg. Examples date back to the end of the Eighties.2 of January 1999 consists of approximately 110. and ease-of-use. however. and Johns Hopkins University in the United States. usually requires some adaption effort to make things work together. This situation is also a  severe hindrance for researchers if they wish to implement and evaluate their algorithms. also called the Gems approach according to the successful Graphics Gems series. Related Work Three approaches of disseminating geometric software can be distinguished: collections of isolated implementations. Integrated applications and workbenches provide a homogeneous environment. we make use of objectoriented solutions and design patterns. PhD students and postdocs. As a consequence. however. CGAL (http://www. for example with animation and interaction capabilities. integrated applications  or workbenches. robustness. the C++ abstractions used by us do not cause any runtime overhead. is one of the precursors of CGAL. However. the constants hidden in the analysis of the  otherwise theoretically efficient algorithms often is not known.  To remedy this situation the Computational Geometry Algorithms Library. The CGAL release 1. C++Gal developed at Inria Sophia-Antipolis. specifically XYZ GeoBench (http://wwwjn.000 lines for accompanying sources. In several appropriate places. and all parts work smoothly together. Duke University. that the library is extensible and that the components can be reused in other projects. a computational geometry library implemented in Java at the Center for Geometric Computing. In some applications. The design goals. They may be created by clicking in a window in a graphical user interface.000 lines of code.cgal. Shortly afterwards. practical implementations usually must address the handling of degeneracies. They state their goal as an effective technology transfer from Computational Geometry to relevant applied fields.geom. The community has addressed these topics from time to time and with increasing intensity (several references are given above).umn. Software libraries promise that the components work seamlessly together.org/) provides a list of publications about CGAL and related research. Another argument is that it is first of all important to understand the general case before treating special cases.  Collecting isolated implementations. led us to opt for the generic programming paradigm using templates in C++. In architecture features of buildings do align on purpose. computational geometry implementations usually use more involved data structures and more advanced algorithms. in academia especially research assistants. comments and empty lines included this time. Overview of the Library Structure file://C:\Users\rmanoj\Desktop\Untitled. The WWW home-page of CGAL (http://www. Thus. but lie on a grid. In terms of the elder Constructive Cost Model (COCOMO) the line counts. These precursors are PlaGeo developed at Utrecht University. not counting C++ comments or empty lines (the release 2. has been started five years ago in Europe in order to provide correct. located at Brown University.htm 1/13/2010 . Switzerland. In practice.inf. what are called degeneracies are even high-valued design criteria.cgal. plus test suite and example programs).org/). A good collection provides the Directory of Computational Geometry Software (http://www. 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. efficient.edu/software/cglist/). This makes adaption harder.ch/geobench/XYZGeoBench. Saarbrücken. a library for combinatorial and geometric computing. The major design goals for CGAL include correctness. and the time schedule indicate a large project comparable to operating systems or database management systems. and reusable implementations [Fabri99]. they tend to be monolithic. but many  useful geometric algorithms have not found their way into the application domains of computational geometry yet.Page 45 of 72 chosen over the real numbers. such as the test suite and example programs. the people involved. the coordinates of the geometric objects may not be randomly chosen over the real numbers.

 direction. circle and more. in d-dimensional space. segment. for example access to the defining objects. An arithmetic is associated with a number type in CGAL and the classes in the geometric kernel are parameterized by number types. which stands apart. where xd != 0. has Cartesian coordinates (x0/xd. line. The geometric kernel is split in three parts. The adaptation of number types from other libraries is contained in the support library as well.. such as point. The modular approach has several benefits: The library is easier to learn. smallest enclosing ellipse and so on. It also contains algorithms. this functionality is not needed by the geometric kernel nor the basic library. lines. and affine transformation.. the implementation work is more easily spread among the project partners. xd). x1. the geometric kernel with basic geometric objects and operations. CGAL advocates the use of exact arithmetic instead of floating point arithmetic.. The support library interfaces the geometric objects with external representations. Geometry in two and three dimensions is well studied and has lots of applications which explains their special status. Among the list of supported formats are VRML and PostScript as well as the GeomView program and LEDA windows for 2D and 3D visualization. ..htm 1/13/2010 . To solve robustness problems. one for three-dimensional objects. for example support for coping with different C++ compilers which all have their own limitations. x1/xd. The homogeneous representation is used for affine geometry in CGAL. to be discussed later. The separation from the kernel and the basic library makes the functionality in the support library orthogonal and therefore open for future extensions. and the basic library with algorithms and data structures. Each type provides a set of member functions. The other family is based on the representation of points using homogeneous coordinates. The support library also contains functionality with non-geometric aspects. The geometric kernel contains simple geometric objects of constant size such as points. for example random points uniformly distributed in a certain domain. In contrast to the core library. The three layers are the core library with basic non-geometric  functionality. 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. Global functions are available for the detection and computation of intersections as well as for  distance computations. Since the arithmetic operations needed in CGAL are quite basic. and one for generaldimensional objects. The core library offers basic non-geometric functionality that is needed in the geometric kernel or the basic library. operations to compute intersections of and distances between objects. The basic library contains more complex geometric objects and data structures: polygons. Other implementations of the traits classes provided in CGAL use the LEDA geometric part. This independence has been achieved with geometric traits classes. For all dimensions there are Cartesian and homogeneous representations available for the coordinates. More formally.Page 46 of 72 CGAL is structured into three layers and a support library. a point with homogeneous coordinates (x0. . It provides geometric predicates on those objects. every library supplying number types can be adapted easily to work with CGAL. the bounding box of the  object if existing. These parts are mostly independent from each other and even independent from the kernel. file://C:\Users\rmanoj\Desktop\Untitled. The figure above indicates the major parts in the basic library. the existence of circles implies that there are also ellipses in the kernel. The support library also contains generators for synthetic test data sets.g. segments. Geometric Kernel The geometric kernel contains types for objects of constant size. Circulators and random number generators belong here as well. preconditions and postconditions. planar maps. and not projective geometry. polyhedra and so on. The core library contains the support for assertions. xd-1/xd). such as for computing the convex hull of a set of points. e. from LEDA or the Gnu Multiple Precision library.. the union of two polygons. one for two-dimensional objects.  The library layers and the support library are further subdivided into smaller modular units. triangles. vector... CGAL provides own number types and supports number types from other sources. and the reduction of dependencies facilitates testing and maintenance.  The current geometric kernel provides two families of geometric objects: One family is based on the representation of points using Cartesian coordinates. tetrahedra. ray. The homogeneous representation extends the representation with Cartesian coordinates by an additional coordinate which is used as a common denominator. like visualizations or external file formats. triangle.g. The kernel objects are closed under affine transformations. Default implementations of the traits classes use the CGAL kernel for the types and primitive operations.. iso-oriented  rectangle and tetrahedron. e. This avoids divisions and reduces many computations in geometric algorithms to calculations over the integers. triangulations. and affine transformations. xd-1.

 boolean operations on polygons and map overlay.  smallest enclosing circle. The internal representations differ considerably in their space requirements and the efficiency of their member functions. One example are affine transformations. It also contains geometric algorithms. They might use polar coordinates or homogeneous coordinates 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. i. if we allow this addition. there are no member functions to set the Cartesian coordinates of a point. polyhedrons. And for the most general representation. Each representation counts the number of objects pointing to it.C for an example point/vector/origin implementation) Class hierarchies are used rarely in CGAL. Both families are parameterized by the number type used to represent the Cartesian or homogeneous coordinates. q. In particular. the representation itself is deleted. If the counter reaches zero by the decrement. Using `copy on write' (a new representation is created for an object whenever its value is changed by a modifying operation). and not to a point as in the previous expression. Copying objects increments the counter of the shared representation. However. The class templates parameterized with CGAL::Cartesian or CGAL::Homogeneous provide the user with a common interface to the  underlying representation. like a two-dimensional point declared as template <class R> CGAL::Point_2. because the member functions are computationally expensive for this general representation. range trees. and sphere. The following program computes the convex hull of 100 random points uniformly distributed in the disc of radius one centered at the origin.r)). Function overloading is used to implement this operation internally as a simple conversion without any overhead. Alternatively we could have used this general representation for affine transformations only. Points are viewed as atomic units. But the use of a hierarchy is justified. namely translation. Another design decision was to make the (constant-size) geometric objects in the kernel non-modifiable (value semantics). and handles (trivial iterator). The implementation of reference counting is simplified by the non-modifiability of the objects. An example of a geometric algorithmic problem is the computation of the convex hull. The list of requirements on the template parameter defines the concept of a representation class for the geometric kernel.  Following the generic programming paradigm as introduced above. reference counting with modifiable objects is possible and only slightly more involved. with a template parameter R for the representation class. rotation and scaling. and no assumption is made on how these objects are represented.e. one can write in CGAL the perfectly legal expression A( p + ( q . CGAL strictly distinguishes points and (mathematical) vectors.Page 47 of 72 where the homogeneous representation is usually known from. such as polygons.   CGAL provides clean mathematical concepts to the user without sacrificing efficiency. For all but the most general representation we gain performance in terms of space and time. circulators. ellipse. These representation types are used as template argument in all geometric kernel types. Then. For example. Points and vectors are not the same with regard to illicit computations resulting from identification of points and vectors in geometric computations. it distinguishes affine geometry from the underlying linear algebra. The slightly different expression A(( p + q) .htm 1/13/2010 .r) contains the illegal addition of two points. CGAL is made to comply with STL. planar maps. since the expression within the affine transformation is meant to evaluate to a vector. However. but gains 2% to 11% runtime for the type leda_real. Note that we do not provide the geometrically invalid addition of two points. We do not even provide automatic conversion between points and vectors but use the geometric concept of an origin instead. so that algorithms and data structures can be easily combined with each other and with those provided by STL and other libraries. and r and an affine transformation A. points and vectors behave differently under affine transformations. Basic Library The basic library contains more complex geometric objects and data structures. To avoid these ambiguities. Objects point to a shared representation. Vectors and points behave differently under affine transformations. and kd-trees. These access functions are provided to make implementing own predicates and operations more convenient. arise frequently in geometric computing. (see Origin. The algorithm takes a set of points and outputs the sequence of extreme points on the boundary of the convex hull.. 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. Like other libraries we use reference counting for the kernel objects. we would expect the same result coordinatewise as in the previous. The type CGAL::Cartesian<double> specifies the Cartesian representation with coordinates of type double. since the specialized representations. the use of reference counting was not the reason for choosing non-modifiability. legal expression. there is no assumption that points are represented with Cartesian coordinates. Typedefs can be used to introduce conveniently short names for the types. and the type CGAL::Homogeneous<int> specifies the homogeneous representation with coordinates of type int. This common interface can be used in higher-level implementations independently of the actual coordinate  representation. But this is not necessarily intended. member functions to set the Cartesian coordinates are expensive. the automatic conversion between points and vectors is not provided as well. segment trees. In particular. The interfaces of geometric objects and data structures in the basic library make extensive use of iterators. the performance penalty caused by the virtual functions is negligible. The point generator gets as parameter a random file://C:\Users\rmanoj\Desktop\Untitled. since this might lead to ambiguous expressions: Assuming three points p. which maintain distinct internal representations specialized on restricted transformations. triangulations (including  Delaunay triangulations). Here is  an example given for the point type with the homogeneous representation and coordinates of type int:   typedef CGAL::Point_2< CGAL::Homogeneous<int> > Point_2. deleting an object decrements the counter of its representation. such as convex hull. Nevertheless. For example. Reference counting costs about 15% to 30% runtime for the types double and float.

htm 1/13/2010 .h" #include <CGAL/Triangulation_euclidean_traits_xy_3.h" int main () { Random Random_points_in_disc_2 rnd(1). It is based on the design in Section Solving Mutual Dependencies between Class Templates. typedef CGAL::Delaunay_triangulation_2<Traits> Triangulation_xy. #include "cartesian_double. pts. Polygon_2 ch. wout). first all points in black. without using any intermediate container to store all input points. Note  that most of these typedefs are equal to those hidden previously in the header file. CGAL::convex_hull_points_2( pts. return 0. This random source is initialized with the fixed seed 1 in this example.begin(). The actual implementation presented here stems from our framework for one sided error file://C:\Users\rmanoj\Desktop\Untitled. delete window. cout << dt.  The following example of a convex hull algorithm illustrates the use of a geometric traits class.h" int main () { Random rnd(1). Window_iterator_point_2 wout( *window).Page 48 of 72 source.  which illustrates the generic tool-box character of STL and its concepts. then the hull as polygon in green and finally the vertices in red.  Triangulations are another example of a container-like data structure in the basic library. Random_points_in_disc_2 rnd_pts( 1. The header file hides the usual typedefs and declares all types parameterized with the representation class CGAL::Cartesian<double>. copy( ch. 100 random points are copied into the triangulation data structure. The following program is therefore even simpler than the previous one. rnd). istream_iterator<Point_3>(). *window << CGAL::GREEN << ch << CGAL::RED. copy( istream_iterator<Point_3>(cin). The revised design of the halfedge data structure for polyhedral  surfaces is described in [Kettner99].vertices_end().vertices_begin(). *window << dt. list<Point_2> pts. ch. back_inserter( pts)). copy_n( rnd_pts. } The triangle-based data structure and the halfedge data structure used for the planar map and the polyhedral surface in the basic library are  based on the design of combinatorial data structures described in [Kettner98]. *window >> p. The back_inserter adaptor of STL is applicable as expected.h> typedef CGAL::Triangulation_euclidean_traits_xy_3<REP> Traits. for example to triangulate a set of three dimensional points with respect to their xyprojection (useful to reconstruct terrains. *window >> p. copy( pts. 100.begin(). return 0. rnd). Window* window = demo_window(). } The major technological achievement in the design of the basic library was the concept of the geometric traits class. see the next Section. Delaunay_triangulation_2 dt. We assume in the following example that the representation class for the geometric kernel is named  REP in the header file. The algorithm used is Andrew's variant of Graham's scan [Andrew79]. } // wait for mouse click This program also illustrates the use of the CGAL polygon as a container class. #include "cartesian_double.end(). wout). rnd_pts( 1. return 0. pts. Triangulations in CGAL support the incremental construction. back_inserter( dt)).  which allows the reuse of the triangulation data structure. 100.  #include "cartesian_double. The result is drawn in a LEDA window. copy_n( rnd_pts. Point_2 p. back_inserter(ch)).0. and writes the triangulation to cout.  Geometric Traits Classes A geometric traits class separates a geometric algorithm or geometric data structure from its underlying geometric kernel. int main () { Triangulation_xy dt.0. The program reads three-dimensional points from cin. Window* window = demo_window(). triangulates them. Point_2 p. The only change is the geometric traits class from  CGAL::Triangulation_euclidean_traits_2 to the one given here.end(). back_inserter( dt)). // wait for mouse click delete window.

 It runs  in linear time and space and can produce up to 2n-2 output points in the degenerate case of all points on a segment where n is the number of  input points. the predicate is equivalent to the sign of the following determinant: In the following example. OutputIterator result) { typedef typename iterator_traits<BidirectionalIterator>::value_type P. *i. the illustration of the use of a geometric traits class in a geometric algorithm.end().x-p. Furthermore this change would distract here from the  purpose of this example.  template <class BidirectionalIterator. hull. return convex_hull( first.pop_back(). 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. and r in the plane is true if the points in this order perform a left turn. while ( traits. } Calling the algorithm with the traits class is straightforward. hull. We use an overloaded definition of the convex hull algorithm  with three parameters to do so.push_back( *i). The algorithm is parameterized with iterators. It requires that the sequence of input points from the range [first. q.begin(). const Point& q. The leftturn predicate for three points p.y-p.y-p.htm 1/13/2010 . }. hull. The algorithm computes the convex hull  and copies all points on the boundary of the convex hull (not only the vertices) in counterclockwise order to the output iterator result. class Traits> OutputIterator convex_hull( BidirectionalIterator first.beyond) of bidirectional  iterators is lexicographically sorted and contains only pairwise disjoint points and at least two points.push_back( *first). file://C:\Users\rmanoj\Desktop\Untitled. OutputIterator result. i != beyond. const Point& r) { return (q. template <class NT> struct Convex_hull_traits { typedef Point_2<NT> Point. result). 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.leftturn()( hull. the geometric traits class for the convex hull algorithm is itself a class template parameterized with the number type NT for the point coordinates. The implementation has been modified to use the iterator based interface from above and the traits class. This is beyond the capabilities of the  currently defined iterator categories and restricts the applicability of the algorithm.push_back( *i). ++i) { while ( traits. BidirectionalIterator beyond.y).x) * (q. result. BidirectionalIterator beyond. hull. } }. The default constructor of the traits class is used. NT is the same type as used for the Cartesian point type Point_2. the geometric traits class is quite short in this example. hull.back())) hull.back())) hull.x-p. } }.x) * (r. for ( --i. We can use iterator_traits  to deduce the point type from the iterator type and from the point type the number type which can be used to provide our geometric traits  class Convex_hull_traits as default argument to the convex hull algorithm. i != first.y) > (r. For points represented in Cartesian coordinates. class OutputIterator. for ( ++i. hull. const Traits& traits) { typedef typename Traits::Point Point.pop_back(). hull. Traits()). template <class NT> struct Point_2 { typedef NT Number_type. typedef typename P::Number_type Number_type. ) { --i. beyond. NT y. NT x.front() = hull.end()[-2]. } // upper convex hull (right to left) i = beyond. } // clean up and copy hull to output iterator hull. Leftturn leftturn() const { return Leftturn(). hull. // sentinel hull. vector<Point> hull. The local vector can be omitted if the algorithm can use the output container as a stack. Thus. return copy( hull. class OutputIterator> OutputIterator convex_hull( BidirectionalIterator first. typedef Convex_hull_traits<Number_type> Traits.pop_back().pop_back().Page 49 of 72 predicates [Kettner98b]. // lower convex hull (left to right) BidirectionalIterator i = first.leftturn()( hull.back(). struct Leftturn { bool operator()( const Point& p.end()[-2].  template <class BidirectionalIterator. *i.push_back( *first).

Design patterns capture the essence of a design solution that has been proven to be useful in practice. Similar to the convex hull algorithm.  In our framework on one sided error predicates [Kettner98b] we introduced the notion of a conservative implementation of a predicate. 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.x) * (r. 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.y-p. Convex_hull_traits_2( double b) : B(b) {} struct Leftturn { double B. another example of a useful traits class with a state is the computation of the two-dimensional convex hull for a set of threedimensional points projected onto a two-dimensional plane.y-p. Instead of projecting the points and computing the convex hull on the projected  points.0. Patterns are not invented.y-p.y)*(r. const Point& q.  However.0) {} }. we do not know the orientation of the points. For more properties of the computed output and other examples see [Kettner98b].(r. The expression to compute the determinant and its error bound give us the conservative implementation (q. Thus. B is arbitrarily set to one.0 / 1048576. Useful applications for such predicates will assume that these false answers occur rarely. just ignoring the elevation in the z-coordinate. Leftturn( double b) : B(b) {} bool operator()( const Point& p.x) * (q. For floating point arithmetic an error bound can be computed such that if the expression computing the determinant is larger than the error bound. A pattern consists of: file://C:\Users\rmanoj\Desktop\Untitled.y) . a geometric class can be used to parameterize the two-dimensional triangulation algorithm to work on the three-dimensional data set.0 / 1048576. the decision errors due to rounding errors in inexact arithmetic are limited to one side of the two possible answers.x-p.y-p. Since B is probably a constant parameter. See the example for CGAL in the previous Section. we round it to (3 2-50 + 2-100) B2 and require B to be a power of two. In order to make the error bound representable as double. } }.(q. with our specialized  conservative predicate traits for the number type double.x) > 8(3u + 6u2 + 4u3 + u4) B2 .y) > (3.0 / 1024. but in principle an implementation saying always false is a legal implementation. the following definition replaces the generic traits. the three points perform a left turn. We have u = 2-53 for IEEE double precision. it might be more appropriate to make it a template parameter in Convex_hull_traits_2. The output can be easily postprocessed with the same algorithm but with an exact implementation of the predicate. patterns are discovered in existing systems. A pattern has to show up in different systems before it is considered to be useful in practice. public: typedef Point_2<double> Point. If a conservative implementation of the leftturn predicate returns true. and u=2-24 for IEEE single precision floating-point numbers. } }. const Point& r) { const double C = 1.htm 1/13/2010 .x)*(r. In this implementation.Page 50 of 72 } One benefit of using function objects in the traits class instead of plain member functions is the possible association of a state with the  function object.  template <> struct Convex_hull_traits<double> : public Convex_hull_traits_2 { Convex_hull_traits() : Convex_hull_traits_2(1. but if it returns false. which assumes. the exact value of the determinant is greater than zero. 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].x-p. //2^-50 return (q. class Convex_hull_traits_2 { double B. We extend this to a traits class with a state. 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.x-p. A common approach is to triangulate the sample points using a Delaunay triangulation in the xy-projection. 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.  Another example of the flexibility of the geometric traits classes is the reconstruction of a terrain from a set of three-dimensional sample points.y) . Just to show a specialization of a class template. Leftturn leftturn() const { return Leftturn(B). the convex hull can be computed with the original three-dimensional points and a modified leftturn predicate that takes into  account the projection stored as a local state.x-p.0 * C + C * C) * B * B.

} { return new Athena_button.cs.htm 1/13/2010 .   Consequences  results.   Solution  classes. Observer.   Problem  the problem statement. static Singleton* instance. The application should be seperated from the decision about the look and feel of its graphical user interface. It makes exchanging a whole family of classes easy. and collaborations. class Motif_button : public Button {}. A static member function is used as a global point of access.wustl. Solution: The singleton class has only private constructors that forbid the creation of any objects outside of the class. class Widget_factory { virtual Window* create_window() = 0. class Athena_window : public Window {}. It returns a pointer to the unique instance of the singleton that is stored internally in a static member variable. class Singleton { Singleton(). The factory itself is also implemented as an abstract base class and a concrete class for each GUI. we ask an object -. or a printer spooler. It uses a pointer to a factory to create concrete instances of widgets.the factory -. Each widget has its own abstract base class and concrete implementations according to the different GUIs. }. Singleton( const Singleton&).edu/~schmidt/patterns. public: static Singleton* Instance(). responsibilities. file://C:\Users\rmanoj\Desktop\Untitled.net/patterns/   WUSTL Patterns page: http://www.to create them for us.html   Abstract Factory Pattern Problem: Consider an application using different graphical user interfaces (GUI) with widgets of different look and feels. Solution: Instead of creating widgets directly. their relationship.. An example are classes representing unique resources in a system. a window manager. 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. Singleton. and provide a global point of access to it. etc. } { return new Motif_button. Singleton Pattern Problem: Ensure a class only has one instance. } { return new Athena_window. class Motif_widget_factory { virtual Window* create_window() virtual Button* create_button() }.   Further information on patterns in the net: Appleton: Essential concepts: http://www.. objects. its context and preconditions. New kinds of widgets are dificult to add. class Motif_window : public Window {}. class Athena_widget_factory { virtual Window* create_window() virtual Button* create_button() }. class Window { virtual ~Window(){} }. { return new Motif_window. virtual ~Widget_factory(){} }. virtual Button* create_button() = 0. class Button { virtual ~Button(){} }. tradeoffs. It promotes consistency among the family.com/bradapp/docs/patterns-intro. } The application uses only pointers to the abstract base classes of the various widgets.cmcrossroads. Consequences: A factory isolates concrete classes. class Athena_button : public Button {}.Page 51 of 72 Name  such as Adapter.html   Patterns Homepage: http://hillside.

// forward declaration struct Geometry. virtual void optimize(). Transform* ). struct Geometry : public Node { virtual void render(). The Visitor classes have a virtual member function for each type in the Node class hierarchy. the objects are of types from a class hierarchy. the canonical solution that is not extendible. and permits other limitation on the number of instances. // optimize Geometry file://C:\Users\rmanoj\Desktop\Untitled. struct Optimize : public Visit { virtual void visit_transform( virtual void visit_geometry ( }.). struct Transform. Consequences: Helps if derivation from a required base class is missing (different libraries etc.Page 52 of 72 Singleton* Singleton::instance = 0. struct Render : public Visit { virtual void visit_transform( virtual void visit_geometry ( }. e. and a set of operations that operate on these objects.k. Geometry* ) = 0.htm 1/13/2010 .a. how much work has the adaptor to do? Visitor Pattern Problem: Given a collection of objects. but not the correct interface.. Tradeoffs. The visitor pattern allows to define new operations without changing the classes of the elements on which it operates. } Consequences: controlled access. Let us define two operations.g. struct Transform : public Node { virtual void render(). Same for sets of template requirements (see adaptors in the STL). return instance. The application now takes an operation object and calls accept of each node object with it. which we make virtual member functions of Node. // forward declaration struct Visitor { virtual void visit_transform( virtual void visit_geometry ( virtual ~Visitor() {} }. virtual void optimize(). both derived from the abstract base class Node. Adaptor Pattern (a. Singleton* Singleton::Instance() { if ( instance == 0) instance = new Singleton. Now. Class A implements its interface by calling the right functions in its object of class C. which performs the operation on that type. This pattern is so simple that I omit an example here. struct Node { virtual void render() = 0. }. virtual ~Node() {} }. Transform* ) = 0. }. This solution is easy to extend with new subclasses of Node. Solution: First. the visitor pattern factors out the operations in a seperate hierarchy of classes. Solution: Write another class A with the correct interface that contains an instance of class C. The boundary is blurred between an adaptor pattern and a class using some other class to implement its functionality. // optimize Transform Geometry* ). all derived from the virtual base class Visitor and each derived class representing one operation. // render Geometry Transform* ). Let us assume a scene graph API with different node types Transform and Geometry. An adaptor does not perform the major part of the task itself. but it is hard to extend with new operations. it is not derived from a required base class. reduced namespace. // render Transform Geometry* ). Wrapper Pattern) Problem: A class C provides the right functionality. 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. virtual void optimize() = 0. render and optimize.

3). 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. } }. return true. here for the polymorphic return type (see also [Weihe98]). t = *wp.base()). } }. Here the example of segment intersection in CGAL: Segment s( Point(1. for example. Consequences: The visitor pattern is easy to extend with new operations in the Visitor hierarchy. 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. class Object { // polymorphic object (smart pointer) Base* p. since the operation is no longer a member function of the object. } Consequences: The original CGAL classes are not influenced by the design decision for intersection computation (locality of this design decision).8)). Solution: We do not impose any restriction on CGAL classes.htm 1/13/2010 .. Remark: In CGAL. Point(1. But we do not want a single global base class and class hierarchy for all classes in CGAL..1). const Object& o) { Wrapper<T>* wp = dynamic_cast<Wrapper<T>*>(o. Point pt. t). a point. The visitor pattern groups related operations together. public: Wrapper(const T& obj) : object(obj) {} Wrapper() {} operator T() { return object. virtual ~Node() {} }. Point(1. result)) cout << "intersection point = " << pt << endl. Visitors can be used to accumulate state. } }. if (wp == 0) return false. class Base { // base class for wrapper classes public: virtual ~Base() {} }. CGAL::Object result = CGAL::intersection( s. but it is hard to extend with new object types. They do not carry the extra vptr and associated costs for RTTI (runtime type information). public: // .Page 53 of 72 struct Node { virtual void accept( Visitor* ) = 0. Template Metaprograms Introduction file://C:\Users\rmanoj\Desktop\Untitled. if (CGAL::assign( pt. struct Geometry : public Node { virtual void accept( Visitor* v) { v->visit_geometry( this). we create an addtional class hierarchy only where we need it. Segment t( Point(1. Instead.5)). a Polymorphic Type in CGAL Problem: We need a polymorphic return type for intersections in CGAL. CGAL::Object. } }. the intersection of two segments can be either emtpy. or a segment. template <class T> // generic wrapper class class Wrapper : public Base { T object. Visitors might require to break some encapsulation (expose more implementation details of the objects). struct Transform : public Node { virtual void accept( Visitor* v) { v->visit_transform( this). template <class T> bool assign(T& t. 8.

0.0).org/blitz/. struct is_prime<0. which can be used to program "at compile time". } }. Among them the following program that computes prime numbers at compile time. The result is communicated with compiler  warnings containing the prime numbers.0> { enum { prim = 1}. template <int i> struct Prime_print { Prime_print<i-1> a. void f() { D<2> d = prim. In [Veldhuizen95b] templates are used to write code such as: // Integrate a function from 0 to 10 DoublePlaceholder x.C // Program by Erwin Unruh template <int i> struct D { D(void*). a library for numerical algorithms with vectors and matrices. // function (f) : associates a value (a) to an object (t) Now observe the similarity with the following function on types :   template < typename T > struct F { typedef typename T::Category type.i-1>::prim }.1> { enum { prim = 1}. } Todd Veldhuizen uses these advanced template techniques with template metaprograms [Veldhuizen95a] and with expression templates  [Veldhuizen95b]. } int a = f(t). file://C:\Users\rmanoj\Desktop\Untitled. Instantiated for a constant number of elements. Let us begin by making an  analogy with the following usual function :   int f(T t) { return t.  In [Veldhuizen95a] a template metaprogram for bubblesort is described.0 + x) is a template expression. i-1>::prim) }. resulting in a single unrolled function for a 256 FFT with all roots evaluated as constants. int i> struct is_prime { enum { prim = ((p%i) && is_prime< (i>2 ? p : 0). enum { prim = is_prime<i. struct is_prime<0. The same technique is used in the paper to implement vector arithmetic more efficiently. void foo() { Prime_print<20> a. double result = integrate( x / (1.htm 1/13/2010 . At some point during the standardization process of C++ it has been discovered that templates make the  C++ compiler actually Turing equivalent at compile time. Compile-time programming The C++ language provides two entities which can be used to program at compile time:   types   constant integral values   We now describe a list of basic operations on these entites. void f() { D<i> d = prim. which we are going to detail later. 10. The term x / (1. This program circulated among the members of the ANSI/ISO C++ standardization committee.  Both techniques are used in Blitz++. The  program does not work anymore on current compilers. template <int p.category(). The second example is a compile time function for sinus and cosinus that can be used to implement a metaprogram for the FFT. }. }.0 + x). 0.  namely avoiding large temporary results in vector expressions. } }.C for a modified version that compiles as promised on the current g++  compiler. }. the metaprogram unrolls the loops of bubblesort and creates the decision tree to sort the elements. }. The first examples using the C++ compiler as an interpreter are credited to Erwin  Unruh.Page 54 of 72 Template metaprogramming refers to a technique where the template instantiation mechanism of the C++ compiler is used to partially  evaluate a program at compile time. Its benefit is that it can be expanded inline in the integrate function. Template  expressions have also nicer syntax than functors. struct Prime_print<2> { enum { prim = 1}. operator int().  // prime. see prime. see http://oonumerics.

-.   template < int i > struct D { enum { value = D<i-1>::value }. &. }. // (G) : associates an integral value (a) to the type (T) typedef H<2>::type A. e. /. template < int i > struct K { enum { value = i+1 }. It also has the property that the expression to which it is applied needs not be defined  (being declared is enough).Page 55 of 72 }. "std::sqrt(2. >>. such "meta" functions are not restricted to only one argument. // function (F) : associates a type (A) to the type (T) Constant integral values can be associated to types and vice-versa. template <> struct D<0> { enum { value = 0 }. |.   template < int i.   Constraining templates using the SFINAE principle file://C:\Users\rmanoj\Desktop\Untitled. return d(i-1). This can be achieved using (eventually partial) specialization. // (H) : associates a type (A) to an integral constant value (2) int a = K<2>::value. typedef Y<T. and not even to one return entity. } // 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 }. !. Note that sizeof() returns an integral constant value. <=. }. ==. }. }. To do real programming. typename U > struct Z { enum { value1 = i+1 }. ^. <<. Built-in operators (+.   template < typename T > struct G { enum { value = 3 }. %. }. enum { value2 = j+i }.). The is_prime example in the introduction illustrates some more possibilities.  Another tool. LargeStruct f(double). typedef F<T>::type A. <. typename T. and we will see later how it allows to extract some properties of  types automatically.htm 1/13/2010 . ||. }. // x is some expression. // Which is equivalent to the run-time program : int d(int i) { if (i == 0) return 0. is the sizeof() operator : struct LargeStruct { char c[2]. }. >. at compile time. }. Overload resolution is a complex mechanism. which allows to connect compile-time programming to the powerful function overload resolution mechanism of C++.. char f(. we need to be able to perform branches. // (+) : associates an integral value (5) to two integral constant values (2 and 3) We can apply composition of meta functions :   int a = G<F<2>::value>::value. template < int s > struct Is_double { enum { value = (s == 1) }. // Composition of F and G. int a = G<T>::value. !=) can also be used to generate  constant integral values directly :   int a = K<2+3>::value. // (K) : associates an integral value (a) to an integral constant value (2) Of course. &&. }.0)" int a = Is_double< sizeof(f(x)) >::value.. template < int i > struct H { typedef H<i+1> type. *. int j. U> type.g. ?:. >=.

it just discards the considered function from the set of possible matching functions. int y) : _x(x). v. There. template < typename T > void f(T const&. trying to substitute float in this overloaded function is going to fail.  Now what happens if we define a nested type type in G ? template < typename T > struct G { typedef int type. template < typename T > void f(T const&). and this is an area of C++ which is very complicated. when that function is  overloaded. Vector_3 const & w) { return Vector_3(v. The remark that is going to save this idea for operators is that the SFINAE principle applies to the whole function signature. typename G<T>::type = 0).  There is a particular feature of the first processing stage performed by the compiler.). int main () { float x. including the return type. In this case. this doesn't work. then this ambiguous case leads  to an error. _y. public: // 2D vector type My_vector(int x. and we get an ambiguity error with the first  template. and the ellipses  version is the one with the least priority. _y(y) {} int x() const { return _x.x() + w.z()). because this template is way too general (it matches almost any type). the process is not yet so nice. The SFINAE principle  implies that the program is perfectly valid. public: // 2D point type file://C:\Users\rmanoj\Desktop\Untitled. So conversion rules come into play.x(). because it matches with equal priority..Page 56 of 72 In C++.y()). // which function f is going to be called ? } The compiler proceeds in 2 stages : it first selects all functions that could match the argument (in this case. to constrain them depending on some properties on types. for example :   template < typename Vector_3 > Vector_3 operator+(Vector_3 const &v.htm 1/13/2010 . This allows to perform some selection on the templates. } }. the other possibilities require conversions which have less priority. v.x(). and that the first template is still chosen. overload resolution is the process by which the compiler selects which specific function to use in a given call.. let's consider concrete types provided by the user.  Now this opens new horizons. because it requires an additional argument to the function (although we can specify a default value). v.x() + w. } Obviously.y() + w.z() + w. } So. }. Now let's add the following additional overloaded function to the previous program : template < typename T > struct G {}. 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. This is called the "Substitution Failure Is Not An Error" (SFINAE) principle. template < typename T > typename G<T>::type f(T const&). f(. Then the second template matches as well (there is no failure during the substitution process). because the template class G can be specialized for some particular types. f(int). then it  discards those which require a conversion which has the least "priority". Consider the following overloaded function f :   void void void void f(). the template version is going  to be chosen because there is no perfect match otherwise. we would like to define generic functions  for adding geometric vectors :   template < typename Vector_2 > Vector_2 operator+(Vector_2 const &v.y() + w. Given that G does not define a nested type type. and it would clash with. f(double). If there is not one unique best match. Vector_2 const & w) { return Vector_2(v. _y. However. to which we would like to apply our generic operator :   class My_vector { int _x. the last 3 declarations). class My_point { int _x. f(x). and this is impossible for overloaded operators.y(). Let us now have a look at how we could make use of this in a practical case : the CGAL library. } int y() const { return _y. and can define or not a nested type type.

bool = T::value > struct Enable_if.   // Primary template template < typename T > struct CompoundType { enum { IsPointer = false. it is easy to enumerate them : template < typename > struct IsFundamental { enum { value = false }. char. a type is not a 2D vector. }. adequate semantics. Now let's try to classify compound types. // By default. template < typename T > struct IsVector_2 { enum { value = false }. }. }. v.. } }.4). We can use  partial specialization to identify some of these categories.x() and .x().x() + w. // But My_vector is a 2D vector. int y) : _x(x). My_vector z = v + w. long. The types My_vector and My_point have identical interfaces. Vector_2 const &w) { return Vector_2(v. // OK My_point p(1. template < typename T. that is. w(3.y() member functions. template < typename Vector_2 > typename Enable_if< IsVector_2<Vector_2>. template <> struct IsVector_2 <My_vector> { enum { value = true }. file://C:\Users\rmanoj\Desktop\Untitled. }. R.4).Page 57 of 72 My_point(int x. We are now going to see ways to write traits classes to automatically extract fundamental properties of types.y() + w. }. Given that there is a small finite number of such types.  So the idea is to constrain the function template. references.htm 1/13/2010 . arrays. in such a way that it will be considered only for types that are 2D geometric vector types. long double and the unsigned versions.). Vector_2 >::type operator+(Vector_2 const &v.  the type of each item for an array).   Type Classification (Note : this section is inspired by chapter 19 of [Vandevoorde03])  We have just seen an example of traits class to express a property of a type. typename. So now we can easily constrain the template using another accessory tool :   template < typename T. }.y()). _y(y) {} int x() const { return _x. q(3. In this case. true> { typedef R type. { enum { value = true }. // error : // no match for `My_point& + My_point&' operator } The whole program can be found here : vector_2. } The following main function illustrates what we finally get :   int main() { My_vector v(1. template <> struct IsFundamental<int> template <> struct IsFundamental<short> { enum { value = true }. IsFunction = false. it is also useful to extract which type(s) the compound is made of (e. IsArray = false. Let's start by writing a traits class which determines if a type is a fundamental type or not. IsPointerToMember = false }. // similarly for bool. and which provide the needed requirements (. and we would like that our generic operator applies only on My_vector.. This kind of thing is typically a job for a traits class. }.g. types which are constructed from other types : plain pointers. correct constructor.. } int y() const { return _y. typename R > struct Enable_if <T..2). template <> struct IsFundamental<double> { enum { value = true }. My_point r = p + q.C.2). IsReference = false.

IsPointerToMember = false }. // Partial specialization for references template < typename T > struct CompoundType<T&> { enum { IsPointer = false. }. IsPointerToMember = false }. We now exploit this fact by trying to  make an array of the given type. // Partial specialization for arrays template < typename T. "Trying". typedef T base_type. function types cannot be recognized that easily. IsReference = false. IsArray = true. // Partial specialization for pointers template < typename T > struct CompoundType<T&> { enum { IsPointer = true.. whereas type promotion between enums and integral types do not have this restriction. }. and the next revision of the standard library may contain such a type_traits mechanism.C). IsReference = false. IsFunction = false. typedef T base_type. }. }. and if it fails. template < typename U > static char test(. template < typename T > struct IsFunction<T&> { enum { value = false }. // Partial specialization for pointers to members template < typename T. However.). Such a possibility would require changes in the core language. IsArray = true. it means the type is one of these special types. IsFunction = false.   template < typename T > class IsFunction { struct LargeStruct { char c[2]. template < typename U > static LargeStruct test(U (*)[1]). IsFunction = false. }. but the curious reader can find the solution in [Vandevoorde03]. IsPointerToMember = true }. template <> struct IsFunction<void> { enum { value = false }. template <> struct IsFunction<void const> { enum { value = false }. IsArray = false. 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. typename C > struct CompoundType<T C::*> { enum { IsPointer = false.. }. because a user defined class could also define a conversion to an integral type. We are therefore going to use the SFINAE principle. after making the following  remark : the only types which cannot be gathered in an array are function types. }. }. IsArray = false.htm 1/13/2010 . typedef T base_type. typedef T base_type. typedef T base_type. // Partial specialization for empty arrays template < typename T > struct CompoundType<T[]> { enum { IsPointer = false. IsReference = true. But we need something more. size_t N > struct CompoundType<T[N]> { enum { IsPointer = false. To conclude : it is possible to write traits classes that determine almost all basic properties of types. }.Page 58 of 72 typedef T base_type. IsArray = false. }. IsFunction = false. Unfortunately. but it is not possible to extract the list of all data members of a class. IsReference = false. void and references. How to determine enumerated types ? What are the features of this types that we could exploit to detect them ? We know that they are  convertible to integral types. Libraries such as Boost provide such mechanisms in an extensive way. IsPointerToMember = false }. here. public: enum { value = sizeof( IsFunction<T>::test<T>(0) ) == 1 }. is a synonym for using the SFINAE  principle. IsPointerToMember = false }. Hint : two user defined conversions cannot be applied automatically consecutively.  We are not going to describe the full code here. The following section illustrates what could be file://C:\Users\rmanoj\Desktop\Untitled. }. IsFunction = false. IsReference = false.

  The trick is the implementation of flatten without knowing the structure of Foo.b = 0.Page 59 of 72 done with such a feature.a = &a2. PolyP allows to write functions over the structure of how types can be defined in the language in general. We need meta information of a type how it is constructed. ls. B* b. } EXAMPLE MAIN PROGRAM USER DATA TYPES The program creates an acyclic graph rooted at a1. for example. ostream_iterator<int>(cout. list<int> ls. }. The missing information is what could be called selfinspection of types. for a struct we need to know its member types. while the leaves with constructor Bar are empty. "Patrik") Foo a is a typical parameterized recursive type definition. The  generic function (called polytypic in PolyP) flatten performs an in-order traversal and concatenates all elements of type a it can find. nodes are labelled with their types.C Example In this section I describe how far one could go with generic algorithms if types would be first class citizens in C++. The variable x contains such a tree.begin(). a1. flatten(a1. a1.chalmers. a2.cs. we cannot. There is only a small set of possebilities how types can be defined in PolyP. }.a = &a3.C for the full program): // ----------------------------------struct B. a3. Self Reflection of Types in C++: The flatten. copy( ls. Can we write such a flatten function in C++? No. They also contain an  integer value. a3.b = &b. // ----------------------------------int main() { A a1. alternatives of types or Cartesian product of types. A a3. here a binary tree. Nodes of type B can just point to a node of type A.i = 3. flatten x) -. For example. " ")).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". a3.a = 0. Then. and nodes of type A contain their integer value:  file://C:\Users\rmanoj\Desktop\Untitled. A* a. a2. cout << endl. B b. a1.ls). struct B { A* a.end(). where the nodes with constructor Doo and the leaves with  constructor Cee contain an element of type a. A a2. Nodes of type A can point to nodes of type A and to nodes of type B.b = 0. The graph looks  as follows. the value we want to concatenate during our flatten call. we could write the following program in C++ (see flatten. a2.expected output: ("Patrik".htm 1/13/2010 . b. and for the running example we provide this information just by hand (as a clever annotation to the type definition). struct A { int i. Let us assume we would have such information.a = &a2.  The following is a correct program in the functional programming language PolyP (see http://www. but almost.i = 1. Instead.i = 2.

} }. we apply flatten  recursively on the dereferenced value of the pointer. T<X.I>()( x.  // ----------------------------------.1> { template <> struct T<A. template <class P. The second argument is the cardinal number of the current member we are describing. class I> struct flatten_items_class<Item. Thus. For the flatten function.3> { int A* B* operator()(A& a) { return a. class I> struct flatten_class<P*. int n. and a way to access the different members. template <class Item. ls). ls). We follow the flatten function call. Note how the UNKNOWN return type causes the recursive enumeration of all the members to stop. X x. The specialization has an operator() that.LEAF / STRUCT WITH SUBITEMS template <class Item. } }. we append the value to the list.b. list<I>& ls) { flatten_items( T<X. T<X. For pointer types.n. we can get away with a rather dense notation that gives us for each structure the types of the members that it contains (incl. class I> void flatten( X x.  // ----------------------------------. list<I>& ls) { flatten_class<X. we distinguish between structs and simple types.push_back(i). returns the member of the given cardinal number. I i.3.3]. // ----------------------------------struct UNKNOWN {}. template <int n. for example. unions.1> { A* operator()(B& b) { return b.push_back(i).I.2> { template <> struct T<A. The first argument is the type we want to annotate. ls). The general class template has an operator() that just returns an object of type UNKNOWN. } }. int n. I i. } }. operator()(A& a) { return a. given an object of that type.I> { // item of type I found void operator()( Item.1>()(x). For each member in each type we write a specialization of the class template. in this implementation of the  flatten function.VALUE / POINTER template <class X. template <class X.2. template <> struct T<B. operator()(A& a) { return a. the value type of the list actually determines what gets collected. } }. list<I>& ls) { ls. class I> struct flatten_items_class { // flatten all subitems void operator()( Item i. x. x.2. We have to add some annotation for our type definitions of A and B to make this example work. The flatten function unrolls along the way types can be defined in C++. list<I>& ls) { if ( x != NULL) flatten_class<P. } }. The output of the program is the sequence [1.i. int i> struct T { UNKNOWN operator()( X&) { return UNKNOWN(). We use again a class template and partial specialization  to do the match. class I> struct flatten_class { void operator()( X x. list<I>& ls) { ls.n. For structs. If the type is simple and equal to the value type of the list.htm 1/13/2010 . class X. } }. flatten_items( T<X.I> { void operator()( UNKNOWN. template <class X. ANNOTATE TYPES FOR SELF-INSPECTION template <class X.a. We use partial specialization of a class template to implement this distinction. We have restricted this example to deal with pointers and structures. list<I>& ls) { flatten( i.Page 60 of 72 The return value of the flatten call is stored in the list ls. we distinguish between pointer  types and non-pointer types. } Second.n+1>()(x). We define a class template with two arguments. ls). } }.  We have omitted. class I> // no (further) subitems file://C:\Users\rmanoj\Desktop\Untitled.I.I> { void operator()( P* x. First.a.} }. class I> // item of type I found struct flatten_items_class<UNKNOWN.int n. ls). we iterate through all members of the struct and call flatten  recursively. template <> struct T<A. Note also that in the case  we want to flatten a struct that has a meta description we get an ambiguous match between the second and fourth specialization. and arrays. The third  specialization solves this ambiguity.I>()(*x. } }.1>(). how many). reference types.n+1>().

This is done by creating an object that will encode the expression in the form of a tree : file://C:\Users\rmanoj\Desktop\Untitled.size(). size_t _s.  The idea is to postpone the actual evaluation of the expression until the assignment operator is seen. i < a. class I> void flatten_items( Item i. by rephrasing some expressions at compile time from a high level description.n.I> { void operator()( UNKNOWN. list<I>& ls) {} }. X x. z). } double & operator[](size_t i) { return _a[i]. if we consider the typical use below.size() == b. return tmp.size()).htm 1/13/2010 . we would like to benefit from the  optimization.  Expression Templates (Note : this section is inspired by chapter 18 of [Vandevoorde03])  Expression templates have several uses..   SArray x. y. } size_t size() const { return _s. we can see that there is an efficiency problem with the last line : a temporary array is created in  order to store the result of x + y. These types represent expressions..C. but it increases the amount of memory  required by the program. list<I>& ls) { flatten_items_class<Item. This is  where expression templates come into play. This is inconvenient because the notation is cumbersome.I>()(i. y. z. _s(n) {} double const& operator[](size_t i) const { return _a[i].x. We can solve the problem by using a dedicated function that adds three arrays in one shot :   SArray add_3(SArray const& a. t. ++i) tmp[i] = a[i] + b[i] + c[i]. x+y*z-z*x. allowing to reach performance that only hand-coded low  level code could have reached.X.size().size() == c. } Now. return tmp. } ~SArray() { delete[] _a. t = add_3(x.   it can be applied to implement problem specific optimizations automatically.   The typical example of expression templates is the optimization of array operations.Page 61 of 72 struct flatten_items_class<UNKNOWN.size() && a. } . SArray const& c) { assert(a.size() == b. class X. public: SArray (size_t n) : _a(new double[n]). This does not increase the number of additions to be performed. but basically the advantages of this technic are : it allows to write complex types in a natural manner. without requiring to write a new function for. } }. SArray tmp(a.size()). or whatever the user of our array class wants to compute. // do something useful with x.. z t = x + y + z. e. but how ugly and unmaintainable it looks in C++. y.. Let's consider :   class SArray { double * _a. and for any expression based on arrays.ls). } This concludes our presentation of flatten. . template <class Item.size(). for (int i = 0. SArray const& b) { assert(a. T<X. The purpose of this example was to stretch the imagination with what would be possible in C++  (and is possible in other languages).g. SArray operator+(SArray const& a.size().n. SArray const& b. i < a. X x. for (int i = 0. which makes it slower due to cache effects. ++i) tmp[i] = a[i] + b[i].n>. int n.X. SArray tmp(a.

size()). 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. +) that it represents.Page 62 of 72 Each node in this tree has a particular type representing the type of operation (e. Array<R2> const& b) { return Array<A_Add<R1.size()). } }. return *this. op2(b) {} // What it is contributing in the final computation double operator[] (size_t i) const { return op1[i] + op2[i]. R2>(a. together with references to its operands. typename R2 > Array<A_Add<R1. // first operand OP2 const& op2. R2> > (A_Add<R1. } // Assignment of arrays of the different type template < typename Rep2 > Array& operator=(Array<Rep2> const& b) { assert(size() == b. and no computation on the arrays at all.   template < typename Rep = SArray > class Array { Rep expr_rep. OP2 const& b) : op1(a). return expr_rep[i].htm 1/13/2010 . template < typename R1.rep(). b. We first need to write a  wrapper around the concrete array SArray. template < typename OP1. for (size_t i = 0.size().g. This is the assignment operator which triggers the recursive computation over the tree. typename OP2 > file://C:\Users\rmanoj\Desktop\Untitled.size(). // second operand public: A_Add (OP1 const& a. } }. R2> > operator+(Array<R1> const& a. for (size_t i = 0. } double operator[] (size_t i) { assert(i < size()). return *this. } Similarly for the other operations :   template < typename OP1.rep())). } Rep & rep() { return expr_rep. } Rep const& rep() const { return expr_rep. ++i) expr_rep[i] = b[i]. ++i) expr_rep[i] = b[i]. i < b. typename OP2 > class A_Add { OP1 const& op1. Notice now how operator+ is going to create a node of the expression tree. i < b.

R2> > (A_Mul<R1. starting from placeholders variables _1... Note that a complete implementation would also have to take care of  some corner cases like proper handling of local variables (by copying them in the tree nodes.. for example in the Boost Lambda library. x = y + x).Page 63 of 72 class A_Mul { OP1 const& op1. provided by the library. // second operand public: A_Mul (OP1 const& a.. but the disadvantage is that the overloading has to know about the R parameter of Array. expression templates are used to avoid creating temporaries. This book contains many more examples that help in understanding the different techniques. } // or. // OK // OK The other possibility is to partially overload the square function as shown below. We can also cite PETE (Portable Expression Template Engine) which is a tool which allows to create expression templates easily. Consider : template < typename T > T square(T const& t) { return t*t. which allows to create functors "on the  fly". In this case. not referencing them). As with some other template technics..  Caveats : for the optimizations to apply. Another caveat of the expression templates is a bad interaction with generic libraries. the return  value is therefore going to be that particular type. // first operand OP2 const& op2. which provides C++ types for computing with multi precision numbers (integers. R2>(a. also paying attention to  cases where the variable being assigned to is also in the arguments (e.rep())). } There are other useful applications of expression templates. } }. OP2 const& b) : op1(a). 9. b. Large Scale C++ Software Design Introduction This section contains material from [Lakos96]. rationals.).. typename R2 > Array<A_Mul<R1. z.. we have achieved what we have promised. _2. } With this implementation. the latter is not being performed by g++ at the moment (version 3. The advantage is that the call sites do not require any  change..  file://C:\Users\rmanoj\Desktop\Untitled.. and split the tree node structures in small components. *_1 > *_2). Inside the square function.. y.   template < typename R > Array<> square(Array<R> const& t) { return t*t..rep(). the compiler needs to perform two kinds of particular optimizations itself : inlining all the small functions which locally create a tree on the stack. . // OK // error The problem here is that x+y doesn't have the "base" type Array<>.htm 1/13/2010 .3).. This way you can write functors on the fly using a natural syntax  like :   std::vector<double*> V. Therefore it has to be explicitely converted before the  call to the square function :   .begin(). R2> > operator*(Array<R1> const& a. R> > square(Array<R> const& t) { return t*t. z = square(Array<>(x+y)). } Array<> x. but there is no possible conversion between any two different "internal" tree node types. which otherwise never shows up in the  usage of this class. taking further advantage of the "optimization" : template < typename R > Array<A_Mul<R. V. For example. z = square(x). op2(b) {} double operator[] (size_t i) const { return op1[i] * op2[i]. but it has the type of a tree node.end(). z = square(x+y). template < typename R1. . it also has the potential to increase compilation times considerably depending on the program. Array<R2> const& b) { return Array<A_Mul<R1. Another application can be found in the GMP library (GNU Multi Precision).  The only possible conversion is from an internal tree node to the "base" type Array<>.g. z = square<Array<> >(x+y). std::sort(V.

C only implements name. Physical Hierarchy The DependsOn relation forms a graph over components. reorganize the parts that participate in the cycle into one compoment (since we do not care what happens within one component). Nowadays.o may be called upon either directly or indirectly to help resolve them at link time.   name. The IsA relation and the HasA relation from the logical design form always compile-time dependencies.h first to assert that name. They will usually contain a few closely related classes and functions. We distinguish between the logical design and the physical design.h and one source file name.  Let us take a closer look on testing. Thus. that name can interact with other translation units at link time.o contains undefined symbols for which x. Compilation times and link times can grow unmanagable large for complex systems. redundant include guards (those  around include statements in the including file as proposed in [Lakos96]) are superfluous. we see techniques how to analyse physical dependencies between packages and how to break up cyclic dependencies between packages.h is needed in order to compile y. Designs with cycles can have much larger compile and link-times for testing.  Components are not restricted to a single class or function. we include name. The logical design describes how to write a class or a function and how to relate them to each other. Internal and External Linkage A name in C++ has internal linkage if it is local to its translation unit and cannot collide with an identical name defined in another translation  unit.h uses include guards to prevent multiple inclusions. Complex systems are hard to understand and hard to test.h (as opposed to just declaring the name again). For example:   • #ifndef NAME_H • #define NAME_H 1 • • // here goes the body of the header file • • #endif // NAME_H // Some compilers als accept a #pragma once statement. We test each compononent in isolation. We assume a test-driver program for each component. A couple of  sanity rules apply:  name.h somewhere else. compilers also detect automatically include guards. Specifically cyclic dependencies increase the complexity. Examples are non-static function names.C..   We are interested in physical dependencies between components (the dependencies within a component are not of interest here). In large systems. Component y exhibits a link-time dependency on x if the object file y. design patterns and template metaprograms. member function names.C. for example. After some definitions.h and name.   A name in C++ has external linkage if. Physical design describes how to organize the code in files. More specifically: Component y exhibits a compile-time dependency on x if x. Examples are type names and static variables. One measure of complexity is the dependency between different units.h is only implemented by name. The benefit of components (and the intended modularization) is hierarchical testing.h includes all header files that are needed when using name. this shows also a way out of cycles between components. Some of the possible ways file://C:\Users\rmanoj\Desktop\Untitled. which basically set up a sane way of organizing source code into components. and non-static global variables.   name. before we test components that depend on this component. in a multi-file program. this does not work if we have cycles in the dependency graph. Of course.htm 1/13/2010 .  All definitions with external linkage in name.C includes name.   Whenever a name of external linkage is used. Lakos96]).and also bigger units of organization.Page 64 of 72 We have seen various implementation aspects for single classes. All components participating in the cycle have to be tested at once and together. We have also seen designs with several classes. Compile-time dependency almost always implies link-time dependency (see also [Page 127 ff. we have to consider a new level of organization: How to distribute classes and functions over files -.C (or whatever suffix for C++ source files is appropriate). The major design rule is: Avoid cycles in the dependency graph! Designs with cycles  are hard to understand. The sanity  rules make it easy to see the physical dependencies from the header file inclusion graph.h. such that a  second attempt to include this header file will not even result in opening and scanning this file. it is crucial to keep the complexity managable. However. If we develop a large application or library.C are declared in name.  A component y DependsOn a component x if x is needed in order to compile or link y. Components and Dependency Relations A component name consists of one header file name.

Total linking cost would be O( n log n). A first attempt might just include the respective constructors. and the normalized cumulative component dependency. int y2).. // . The cost for linking all test-drivers can be captured in a useful metric. However. The CCD of a perfectly balanced binary dependency tree of n components is (n+1) * log2(n+1) . Rectangle( const Window& w).  An examples: We are given a bunch of geometric objects. #endif // WINDOW_H // We realize. is the sum over all components Ci in a subsystem of the number of components needed in order to test each Ci incrementally. public: Rectangle( int x1. The book [Lakos96] describes tools to analyse the dependencies of components and to compute these metrics automatically. int yCenter. // . int y1.com/cp/lakos/...  // rectangle.h #ifndef WINDOW_H #define WINDOW_H 1 class Window { // .. no component depends on any other component. The Cumulative Component Dependency. Derived metrics are the avarage component dependency.h #ifndef RECTANGLE_H #define RECTANGLE_H 1 #include "window..h #ifndef WINDOW_H file://C:\Users\rmanoj\Desktop\Untitled. and second. A well designed system will aim for a flat acyclic hierarchy. each component depends on all other components. #endif // RECTANGLE_H // // window. }.. Reducing Link-Time Dependencies: Levelization We introduce several techniques for eliminating cyclic dependencies in the dependency graph.n..aw. each non-trivial realistic system will have dependencies.h" class Rectangle { // . 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. int width. but as the design evolves over time cycles are introduced. We compare two worst cases for n components: First. we have to include the respective header  files and have a cyclic dependency.. public: Window( int xCenter. approximately shaped like a balanced tree.. The sources for the tools are available at ftp://ftp. that both represent (among others) a two-dimensional box and we would like to be able to construct a rectangle from a window  and vice versa. The underlying assumption is that an initial  design is actually likely to be free of cycles. int x2. the link time for building all test drivers increases. }.htm 1/13/2010 . int y2).. assuming the above rules for packages have been followed. which is the CCD devided by the CCD of a perfectly balanced binary dependency tree with the same number of components. Furthermore. int height). // . among others a rectangle in a component of the same name: // rectangle. int y1.h #ifndef RECTANGLE_H #define RECTANGLE_H 1 class Rectangle { // . CCD. and O( n) in the second case. public: Rectangle( int x1. }. But as a consequence. #endif // RECTANGLE_H // We also work with a graphical user interface and have a component with a class for a window:  // window.Page 65 of 72 of reorganizing a design are covered in the next section. int x2. ACD = CCD / n.. NCCD..

Thus. int height). // . since we include the other header file before declaring the own class. int y1. public: Window( int xCenter. Escalation file://C:\Users\rmanoj\Desktop\Untitled.h" class Window { // ... The resulting dependency graph shows that we still have the cyclic dependency between the components. // .. if we follow the include statements and the include guards. public: Window( int xCenter. In fact.C and window. our example so far is not levelizable. int height).. We will see now some techniques to break cycles and to make a design levelizable. public: Rectangle( int x1.. int yCenter. }..Page 66 of 72 #define WINDOW_H 1 #include "rectangle. not the link-time dependencies.. int width.C files) is acyclic. #endif // WINDOW_H // However. int x2. int width. we will find out that the solution does not compile yet. We just have  reduced the compile-time dependencies... since we use the class Window only by reference in the Rectangle constructor. Window( const Rectangle& r). in order to implement the constructors in the source files rectangle...C the respectively other header file has to be  included again. class Rectangle { // . but a declaration would be sufficient. int yCenter. Rectangle( const Window& w). #endif // RECTANGLE_H // // window. int y2). which we get by including the header file. Window( const Rectangle& r). }. #endif // WINDOW_H // The dependancy graph looks like this:  Actually.h #ifndef RECTANGLE_H #define RECTANGLE_H 1 class Window. // . }.  Definition: A subsystem is levelizable if it compiles and the graph implied by the include directives of the individual components (including the . we do not need the full definition of the class Window. We need to use forward declarations to solve this problem. class Window { // . The same is true for the class Rectangle in the Window header file. // rectangle.h #ifndef WINDOW_H #define WINDOW_H 1 class Rectangle.htm 1/13/2010 ..

// .  In our example we introduce the component.Page 67 of 72 Escalation breaks a cycle by lifting the interdependent functionality one level up into a new component. It does not work nicely with our running example. here as static member functions of a  class. Thus. file://C:\Users\rmanoj\Desktop\Untitled. the extracted functionality is small enough to be put in a single component. Examples for in name only are reference and pointer types.h #ifndef CELL_H #define CELL_H 1 class Cell { char* d_name.h #ifndef CELL_H #define CELL_H 1 #include class Cell { std::string d_name.  // boxutil. However. The name has been  implemented as a string. The interdependent functionality is  supposed to be small compared to the involved components. but to reduce coupling and dependencies in general. But instead of collecting the interdependent functionality in a component in a level up. static Rectangle toWindow( const Window& w). Cell& operator=( const Cell& cell). Redundancy This is not necessarily a technique to break cycles. public: Cell( const char* name).  // cell. public: Cell( const char* name). ~Cell(). and handles that just pass their data as pointers around. independently of the named object. Examples are container classes. boxutil. #endif // CELL_H // Here it might be worthwhile to reimplement the small fraction we need from string. it might be worthwhile to reimplement this small  fraction again in the other component. that contains only the two conversion functions. The interdependent functionality is isolated and repackaged in components.  // cell. const char* name() const. nodes.. Components that use objects in name only can be thoroughly tested.  Opaque Pointers Definition: A function f uses a type T in size if compiling the body of f requires having first seen the definition of T. const char* name() const. whenever only a small  fraction of a component is actually used in another component and causes the dependency.  Factoring Factoring is the general version of escalation and demotion. Cell( const Cell& name). }. namely storing a dynamically allocated array of characters. // . }. the goal is to reduce the complexity of the remaining cycle. struct Boxutil { static Window toWindow( const Rectangle& r). class Window.. we collect the  functionality one level down..htm 1/13/2010 .h #ifndef BOXUTIL_H #define BOXUTIL_H 1 class Rectangle.. Consider an example of a cell class. not  necessarily in a single component.. // .. // . The rectangle and the window component remain untouched.  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. }... Both definitions extend naturally for components. #endif // BOXUTIL_H // Demotion Demotion is similar to escalation. The idea is. that contains among others a name. but is still presented at the interface of cell as an old C-style char* pointer.

Full insulation is usually not appropriate at the bottom layers of a library. and   3.h #ifndef INSULATED_H #ifndef INSULATED_H file://C:\Users\rmanoj\Desktop\Untitled. If the private member function can be implemented using the public interface of the class.   Default arguments. references to the private member variables can be added to the function signature. such as the assignment operator.Page 68 of 72 #endif // CELL_H // However.  A private member function can be changed to a static non-local function of the component. all member functions other than the destructor including inherited functions. If the member function needs exclusive access to private member variables. Memory use can increase with dynamic memory or virtual tables.  even if they perform the same as the default implementation would.  Callback Callback functions allow to break a cycle. size_t nmemb. const void *)) The function pointer compar is the callback function. But sometimes. it has a non-inline virtual destructor defined with an empty implementation. However. or the simple qsort function in the standard C library:  NAME qsort . but not HoldsA relationship. callback functions are difficult to understand.   Inline functions and inline member functions.  Reducing Compile-Time Dependencies: Insulation We give a list of parts in a class that can create compile-time dependencies. it neither contains nor inherits from classes that contain member data.sorts an array SYNOPSIS #include void qsort(void *base. we just need to add the this pointer as an explicit function argument. we address two techniques for partial insulation in the next section. Several of the design patterns have  protocol classes as part of their design. Major runtime costs for insulation can happen because inline functions are no longer possible.   Insulation techniques eliminate the above dependencies. Protocol classes in C++ are similar to interfaces in Java. compiler generated member functions can be explicitly implemented. 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. or private (or protected) members of any  kind.  Another technique for full insulation is an opaque pointer.   Enumerations. // Insulated. Typical example are graphical user interfaces. Two techniques for full insulation are covered in the section thereafter. are declared pure virtual and left undefined. for example the adaptor pattern. Full insulation is appropriate at the higher layers of a library that are exposed to the users. and that the old C-style interface is actually a bit clunky. The cost for this is usually dynamic memory management.   A protocol class is a nearly perfect insulator. non-virtual functions. Techniques for Full Insulation Definition: An abstract class is a protocol class if   1. In case that a future revision would like to change this semantics it could  be implemented without recompiling dependent components. which is by name only). going from partial insulation to full insulation is very easy. int (*compar)(const void *. Sometimes. size_t size.   2. Techniques for Partial Insulation A HasA relationship can be changed to a HoldsA relationship. debug.   Layering (HasA relationship.   Include directives. private inheritance). A compile-time dependency exist for another component using  this component if the other component has to be recompiled if one of the following parts in this component changes:  Base class (incl. and maintain. virtual dispatch tables can add another indirection. for example. The insulated class contains only one opaque pointer to its private data and no other member variables.   Private and protected members. it is arguable for this example that a dependency on strings is not a big issue. and dynamic allocation of memory is slow. the last 5 percent are the hardest and the most costly.  Besides the obvious ones.htm 1/13/2010 .   Compiler generated member functions.

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. They form a loose set of classes. while the container classes are implemented in the object-oriented way. Here. value-based and pointer-based collections. The template-based collections are availbale in three versions.. string operator+ ( const string& s1. The return type causes problems if we derive a class from string and use this operator. It has about 40000 lines of code. and C-lib wrappers. such as complex numbers and strings. Lippman96]. All emphasize the separation of policy and implementation. Consider the file://C:\Users\rmanoj\Desktop\Untitled.C #include "Insulated. 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. special_string s3 = s1 + s2. It contains strings..htm 1/13/2010 . See [page 33ff. it pays off once we use number types that are bigger than the builtin number types. The . const string& s2). The class string is not suitable for subclassing. s2. However.  An emphasized distinction was between the view on abstract data types with value semantics and object-oriented implementations with state changing operations. constructors and member functions }. are implemented with value semantics. Todd Veldhuizen (author of Blitz++) was an intern at Rogue Wave. we had the discussion in CGAL with reference counting for the small kernel objects. maps and similar container classes.C file can be changed and recompiled without  forcing any other component to recompile.  Gnu C++ Library The libg++ was one of the first available C++ libraries.h" class Insulated_private { // .. A sister library from these inhouse  developments is Math.h facilities). which motivated his work on efficient vector  expressions in Blitz++. // type error We get a type error because we cannot assign a string to a special_string. }. Overview of Some Foundation Libraries Introduction The following overview highlights only a few aspects of the different C++ libraries that are of interest for this course. void foo() { special_string s1.  Tools.  // Insulated.  The string class was reference counted at the beginning using copy-on-write for modifying accesses. 10. Each container class is provided in three forms. the classes in Tools.. container classes.) Tools.Page 69 of 72 class Insulated_private. See [page 43ff. intrusive. To lessen the  temptation to derive from string. storage allocators. as a generic implementation based on the C-preprocessor (based on the genric. the developers of libg++ gave the string class a rich (not to say fat) interface. and as a heterogeneous collection (Smalltalk like). All libraries implement  strings.h++ evolved out of inhouse developments at Rogue Wave 1987. // . Smaller classes.. class Insulated { Insulated_private* d_data.h++ Tools.h++.. (An interesting yet exceptional opinion. However. }.C file implements the private data type and all member functions and constructors. Lippman96]. // opaque pointer public: // .h++ are no derived from a common base class. as a class template.h++ provides container classes following the abstractions of the Smalltalk class hierarchy. lists. #endif // INSULATED_H // The .. I have not seen this opinion anywhere else yet. number types. See for example an operator + to concatenate two strings: class string. IO  streams.  class special_string : public string { .. It became available first in 1985..

queues. } } } } } file://C:\Users\rmanoj\Desktop\Untitled.. Programs in LEDA look similar to their pseudo-code counterparts in text books.. 1984 - 1987. Polylithic data structures can share substructures. and Simple_Vector allocating its array from the stack. and the implementation can provide different forms of synchronisation for multi-threaded program executions. rings. . Booch Components The Booch components started as an ADA library.  The library contains monolithic data structures. Lippman96]. or emphasizing LEDA's performance and often worst-case optimality "algorithm + LEDA = efficient program". LEDA. and usually provides several implementations of the same data structure. However. . strings. They consisted of 501 packages with about 150000 lines of code. Value. When copied. However. So this report doesn't seem to be a recommendation for Ada. The implementation can be bounded or unbounded.. Each container class can have several different implementations. LEDA covers algorithms and data structure of most text books. LEDA claims to fulfill the equation "algorithm + LEDA = program".. for dictionaries or priority queues. container) forall( elems. and polylithic data structures.and pointer-based lists can handle arbitrary item types. . For the ease-of-use LEDA defines convenient macros for for-loops. . graph) forall_edges( edge.4.. a monolithic data structures perform a deep-copy (copy all of its contained items).. the Library of Efficient Datatypes and Algorithms LEDA started 1988 at the Max-Planck Institute in Saarbrücken. LEDA uses a concept of items that is similar to iterators. Polymorphic iterators increased the flexibility and functionality of the C++ library and reduced its size by about 50%. The set of available implementations have been rigorously factored out and reduced to three different implementations: Simple_List allocating its nodes on the heap.g. future releases are expected to add more functionality that will finally increase the NCSL  count again. This code size reduction was even accompanied by an increase of  flexibility and functionality of the library. bags. This rigoruous factoring reduced the library size coming from Ada by 20-30%. which are lists. [Mehlhorn99]. Multi-threaded support is factored into a lock-class. just as the STL container classes do. node) { { { { { . The value-based list copies the items into internal nodes. sets.. See [page 59ff. Germany. . this  count includes the comments. Therefore. Factoring memory allocation and its synchronisation into its own components saved another 50%. node priority queues. a polylithic data structures performs a shallow copy (it just creates pointers to the original parts).7 and to 15000 NCSL with release 2. A main difference to iterators is that the access of another item from the current item (e. or graph nodes and edges.. trees and graphs.htm 1/13/2010 . for  example. Moving to lock-classes saved another 50%.Page 70 of 72 example of a list.  An important design goal for LEDA is ease-of-use. deques. Resource allocation and de-allocation is handled in the constructor and the destructor respectively. The pointer-based list keeps only a pointer to the items. which are sublists. container) forall_nodes( node. graph) forall_adj_edges( edge.0 it consists of 210000 lines of code... which is about  125000 lines of non-comment source lines (NCSL). In the release 4.  Data types   Number types   Graphs and supporting data structures (node arrays. The library distinguishes between the abstract data types (the interfaces) and the concrete data types (the implementation). Simple_Bounded_List allocating a limited number of nodes from the stack.. going to the successor item in a list) requires the container class. The first C++ release in 1990 was already shortened to 17000 NCSL and continued to shrink  to 15000 NCSL with release 1. here some examples: forall_items( item. Virtual constructors addresses memory allocation schemes. An intrusive list assumes that the items have already the pointers needed to link them into the list.)   Graph algorithms   Graph editor   Geometric objects   Geometric algorithms   2d-and 3d-visualization   LEDA aims for the (theoretical and practical) fastest solution. which are stacks.0. which contain also the reference manual of LEDA. maps.. subtrees. When copied. Strings are reference counted using copy-on-write for modifying functions.

Second. we get the following inheritance relationship: pair_impl <-----pair<T> ^ | | Impl <-----pair_x<T. } } void* min() { return d_min. the base class that can be compiled and put into a library. the (privately) derived class template. skip lists. the overhead in memory size is claimed to be  less than two and in runtime less than three compared to hand-coded solutions. such as a dictionary. a different name has been given to the dictionary type where the user can select from different implementations. // note.   2. The  derived class template builds a wrapper around the base class that does the safe type casts between void* pointers and the user data. Templates not available when LEDA started. For this.Impl> file://C:\Users\rmanoj\Desktop\Untitled. 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). for example.b)) { d_min = a.   The current design in LEDA (version 3.E>.   3. void* b) = 0. d_max = a. (see also leda_impl. For LEDA. new T(b)). and possible  implementations. Both dictionaries are implemented using the private inheritance from the implementation. const T& b) { set( new T(a).C) class pair_impl { void* d_min. void* b) { if ( cmp(a.  The default dictionary array in LEDA is called d_array<I. and still choose a specific implementation when using it. The dictionary with implementation parameter is (publicly) derived from the default dictionary and its member functions are virtual. Here is a small example for an ordered pair as a data structure. } else { d_min = b. a use can write generic algorithms based on the default dictionary. Using above example with the ordered pair data structure. We ignore the proper copy and destruction functionality here. protected: // this class is only ment for derivation // to be implemented in derived class: virtual bool cmp( void* a. Since LEDA does not rely on template default arguments. void* b) { // use casts to T return * static_cast<T*>(a) < * static_cast<T*>(b). and the derived class template performs the required operation after casting the void* pointers back to the user data. It implements the comparison required by the base class. It can be compiled and distributed in object-code format. Source code must be exposed to library client.  Instead. such as balanced trees.Page 71 of 72 Ease-of-use and flexibility can come with a price tag on the performance of the library. This separation has also been mentioned as policy versus implementation for the other  libraries.0 and 4.E. _d_array<I. } }. void* d_max. The base class implements  as much functionality as possible based on void* pointers for the user data. Thus. Long compilation times for templates. d_max = b. For the simplicity of presentation here it is implemented inline. } }. this derivation has been chosen as a compact solution to provide the base class with some knowledge how to operate on the user data. where I is the index type and E is the associated element type. void set( void* a.htm 1/13/2010 . This separation into derived class templates and base classes coincide with the separation into data types. how to copy and how to compare it. the base class defines abstract virtual functions accepting void* pointers. Note that the void* pointer storage requires dynamic allocation. etc. } T& max() { return * static_cast<T*>( pair_impl::max()). The comparison function is implemented using a virtual member function. } T& min() { return * static_cast<T*>( pair_impl::min()). } void* max() { return d_max. The  derivation is actually a private derivation and wouldn't be necessary for the wrapper functionality. First.Impl>.  The designers of LEDA did not choose to implement data structures as templates for the following reasons: 1.0) splits data structures into a base class and a derived class template. using dynamic binding at runtime. } public: pair( const T& a.  template <class T> class pair : private pair_impl { virtual bool cmp( void* a.

 we can continue with the derived class including an implementation parameter. 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. public pair<T> { virtual bool cmp( void* a. } public: pair_x( const T& a. Only for big data types. } }. which is handcoded for integers to call the operator < directly. LEDA uses a smart pointer. LEDA distinguishes between big data types and small data types. called GenPtr. this path for optimizing LEDA's performance is not readily available in the documentation for the library users. choose a builtin type that fits into a pointer (e. (see  also leda_impl2. LEDA partially compensates these inefficiencies with two optimizations. or write a trivial class slightly larger than a pointer. LEDA uses dynamic memory.  There are two major sources of inefficiencies in this design: the dynamic memory allocation and virtual function calls for basic operations. void* b) { // use casts to T return * static_cast<T*>(a) < * static_cast<T*>(b). An example is the binary search in ordered dictionaries.Page 72 of 72 Since Impl could be equal to pair_impl. we have to make the private inheritance virtual. such as comparisons. const T& b) { Impl::set( new T(a). Small data types are small enough to fit into the size of a pointer. file://C:\Users\rmanoj\Desktop\Untitled. thus carrying an unused implementation and its space around. Note that this solution derives actually from two implementations. when comparing with LEDA. class Impl> class pair_x : private virtual Impl. Though it could be done already by deriving from a container class and rewriting the member functions in question.htm 1/13/2010 .C for the full example)  template <class T. Assuming we also had made the min and max  member functions of our pair class from above virtual. However. } virtual T& max() { return * static_cast<T*>( Impl::max()). } virtual T& min() { return * static_cast<T*>( Impl::min()). to see the full overhead of LEDAs design. which is then drawn from an efficiently self-managed pool of memory. new T(b)).g. The first optimization gets rid of dynamic memory management for small data types. to actually store small data types in the place of the pointer instead of allocating memory. int) to benefit from LEDAs optimizations. In conclusion.

Sign up to vote on this title
UsefulNot useful