From 2849154a60ad85aa8c0a54660d3c46bc041dff66 Mon Sep 17 00:00:00 2001 From: Andreas Lauser Date: Wed, 10 Nov 2010 14:24:23 +0000 Subject: [PATCH] handbook: factor the design patterns chapter out into a separate file --- doc/handbook/designpatterns.tex | 480 ++++++++++++++++++++++++++++++++ doc/handbook/dumux-handbook.tex | 1 + doc/handbook/propertysystem.tex | 476 ------------------------------- 3 files changed, 481 insertions(+), 476 deletions(-) create mode 100644 doc/handbook/designpatterns.tex diff --git a/doc/handbook/designpatterns.tex b/doc/handbook/designpatterns.tex new file mode 100644 index 000000000..68da24f33 --- /dev/null +++ b/doc/handbook/designpatterns.tex @@ -0,0 +1,480 @@ +\chapter{Design Patterns} +\label{chap:DesignPatterns} + +This chapter tries to give a high-level understanding of some of the +fundamental techniques which are used by \Dune and \Dumux and the +motivation behind these design decisions. It is assumed that the +reader already has basic experience in object oriented programming +with C++. + +First, a quick motivation of C++ template programming is given. Then +follows an introduction to polymorphism and the opportunities for it +opened by the template mechanism. After that, some drawbacks +associated with template programming are discussed, in particular the +blow-up of identifier names and proliferation of template arguments +and some approaches on how to deal with these problems. + +\section{C++ Template Programming} + +One of the main features of modern versions of the C++ programming +language is robust support for templates. Templates are a mechanism +for code generation build directly into the compiler. For the +motivation behind templates, consider a linked list of \texttt{double} +values which could be implemented like this: +\begin{lstlisting}[basicstyle=\ttfamily\scriptsize,numbers=left,numberstyle=\tiny, numbersep=5pt] +struct DoubleList { + DoubleList(const double &val, DoubleList *prevNode = 0) + { value = val; if (prevNode) prevNode->next = this; }; + double value; + DoubleList *next; +}; +int main() { + DoubleList *head, *tail; + head = tail = new DoubleList(1.23); + tail = new DoubleList(2.34, tail); + tail = new DoubleList(3.56, tail); +}; +\end{lstlisting} +But what should be done if a list of strings is also required? The +only ``clean'' way to achive this without templates would be to copy +\texttt{DoubleList}, then rename it and change the type of the +\texttt{value} attribute. It is obvious that this is a very +cumbersome, error-prone and unproductive process. For this reason, +recent standards of the C++ programming language specify the template +mechanism, which is a way let the compiler do the tedious work. Using +templates, a generic linked list can be implemented like this: +\begin{lstlisting}[basicstyle=\ttfamily\scriptsize,numbers=left,numberstyle=\tiny, numbersep=5pt] +template +struct List { + List(const ValueType &val, List *prevNode = 0) + { value = val; if (prevNode) prevNode->next = this; }; + ValueType value; + List *next; +}; +int main() { + typedef List DoubleList; + DoubleList *head, *tail; + head = tail = new DoubleList(1.23); + tail = new DoubleList(2.34, tail); + tail = new DoubleList(3.56, tail); + + typedef List StringList; + StringList *head2, *tail2; + head2 = tail2 = new StringList("Hello"); + tail2 = new StringList(", ", tail2); + tail2 = new StringList("World!", tail2); +}; +\end{lstlisting} + +Compared to approaches which use external tools for code generation -- +which is the approach chosen for example by the +FEniCS~\cite{FENICS-HP} project -- or heavy (ab)use of the C +preprocessor -- as done for example by the UG framework~\cite{UG-HP} +-- templates have several advantages: +\begin{description} +\item[Well Programmable:] Programming errors are directly detected by + the C++ compiler. Thus, diagnostic messages from the compiler are + directly useful because the source code which gets compiled is the + same as the one written by the developer. This is not the case + for code generators and C-preprocessor macros where the actual + statements processed by the compiler are obfuscated. +\item[Easily Debugable:] Programs which use the template mechanism can be + debugged almost as easily as C++ programs which do not use + templates. This is due to the fact that the debugger always knows + the ``real'' source file and line number. +\end{description} +For these reasons \Dune and \Dumux extensively use the template +mechanism. Both projects also try to avoid duplicating functionality +provided by the Standard Template Library (STL,~\cite{STL-REF-HP}) +which is part of the C++ standard and functionality provided by the +quasi-standard Boost~\cite{BOOST-HP} libraries. + +\section{Polymorphism} + +In object oriented programming, some methods often makes sense for all +classes in a hierarchy, but what actually needs to be \textit{done} +can differ for each concrete class. This observation motivates +\textit{polymorphism}. Fundamentally, polymorphism means all +techniques where a method call results in the processor executing code +which is specific to the type of object for which the method is +called\footnote{This is \textit{poly} of polymorphism: There are + multiple ways to achieve the same goal.}. + +In C++, there are two common ways to achieve polymorphism: The +traditional dynamic polymorphism which does not require template +programming, and static polymorphism which is made possible by the +template mechanism. + +\subsection*{Dynamic Polymorphism} + +To utilize \textit{dynamic polymorphism} in C++, the polymorphic +methods are marked with the \texttt{virtual} keyword in the base +class. Internally, the compiler realizes dynamic polymorphism by +storing a pointer to a so-called \texttt{vtable} within each object of +polymorphic classes. The \texttt{vtable} itself stores the entry point +of each method which is declared \texttt{virtual}. If such a method is +called on an object, the compiler generates code which retrieves the +method's memory address from the object's \texttt{vtable} and then +continues execution at this address. This explains why this mechanism +is called \textbf{dynamic} polymorphism: the code which is actually +executed is dynamically determined at run time. + +\begin{example} + \label{example:DynPoly} + A class called \texttt{Car} could feature the methods + \texttt{gasUsage}, which by default corrosponds to the current + $CO_2$ emission goal of the European Union but can be changed by + classes representing actual cars. Also, a method called + \texttt{fuelTankSize} makes sense for all cars, but since there is + no useful default, its \texttt{vtable} entry is set to $0$ in the + base class. This tells the compiler that it is mandatorily that this + method gets defined in derived classes. Finally, the method + \texttt{range} may calculate the expected remaining kilometers the + car can drive given a fill level of the fuel tank. Since the + \texttt{range} method can retrieve information it needs, it does not + need to be polymorphic. +\begin{lstlisting}[basicstyle=\ttfamily\scriptsize,numbers=left,numberstyle=\tiny, numbersep=5pt] +// The base class +class Car +{public: + virtual double gasUsage() + { return 4.5; }; + virtual double fuelTankSize() = 0; + + double range(double fuelTankFillLevel) + { return 100*fuelTankFillLevel*fuelTankSize()/gasUsage(); } +}; +\end{lstlisting} + +\noindent +Actual car models can now be derived from the base class like this: +\begin{lstlisting}[basicstyle=\ttfamily\scriptsize,numbers=left,numberstyle=\tiny, numbersep=5pt] +// A Mercedes S-class car +class S : public Car +{public: + virtual double gasUsage() { return 9.0; }; + virtual double fuelTankSize() { return 65.0; }; +}; + +// A VW Lupo +class Lupo : public Car +{public: + virtual double gasUsage() { return 2.99; }; + virtual double fuelTankSize() { return 30.0; }; +}; +\end{lstlisting} + +\noindent +Calling the \texttt{range} method yields the correct result for any +kind of car: +\begin{lstlisting}[basicstyle=\ttfamily\scriptsize,numbers=left,numberstyle=\tiny, numbersep=5pt] +void printMaxRange(Car &car) +{ std::cout << "Maximum Range: " << car.range(1.00) << "\n"; } + +int main() +{ + Lupo lupo; + S s; + std::cout << "VW Lupo:"; + std::cout << "Median range: " << lupo.range(0.50) << "\n"; + printMaxRange(lupo); + std::cout << "Mercedes S-Class:"; + std::cout << "Median range: " << s.range(0.50) << "\n"; + printMaxRange(s); +} +\end{lstlisting} + +For both types of cars, \texttt{Lupo} and \texttt{S} the +\texttt{printMaxRange} function works as expected, yielding +$1003.3\;\mathrm{km}$ for the Lupo and $722.2\;\mathrm{km}$ for the +S-Class. +\end{example} + +\begin{exc} +What happens if \dots +\begin{itemize} +\item \dots the \texttt{gasUsage} method is removed from the \texttt{Lupo} class? +\item \dots the \texttt{virtual} qualifier is removed in front of the + \texttt{gasUsage} method in the base class? +\item \dots the \texttt{fuelTankSize} method is removed from the \texttt{Lupo} class? +\item \dots the \texttt{range} method in the \texttt{S} class is + overwritten? +\end{itemize} +\end{exc} + +\subsection*{Static Polymorphism} + +Dynamic polymorphism has a few disadvantages. The most relevant in the +context of \Dumux is that the compiler can not see ``inside'' the +called methods and thus cannot optimize properly. For example, modern +C++ compilers 'inline' short methods, i.e. they copy the method's body +to where it is called. First, inlining allows to save a few +instructions by avoiding to jump into and out of the method. Second, +and more importantly, inlining also allows further optimizations which +depend on specific properties of the function arguments (e.g. constant +value elimination) or the contents of the function body (e.g. loop +unrolling). Unfortunately, inlining and other cross-method +optimizations are made next to impossible by dynamic +polymorphism. This is because these optimizations are done by the +compiler (i.e. at compile time) whilst the code which actually gets +executed is only determined at run time for \texttt{virtual} +methods. To overcome this issue, template programming can be used to +achive polymorphism at compile time. This works by supplying the type +of the derived class as an additional template parameter to the base +class. Whenever the base class needs to call back the derived class, +the \texttt{this} pointer of the base class is reinterpreted as a +being a pointer to an object of the derived class and the method is +then called on the reinterpreted pointer. This scheme gives the C++ +compiler complete transparency of the code executed and thus opens +much better optimization oportunities. Since this mechanism completely +happens at compile time, it is called ``static polymorphism'' because +the called method cannot be dynamically changed at runtime. +\begin{example} + Using static polymorphism, the base class of example \ref{example:DynPoly} + can be implemented like: +\begin{lstlisting}[name=staticcars,basicstyle=\ttfamily\scriptsize,numbers=left,numberstyle=\tiny, numbersep=5pt] +// The base class. The 'Imp' template parameter is the +// type of the implementation, i.e. the derived class +template +class Car +{public: + double gasUsage() + { return 4.5; }; + double fuelTankSize() + { throw "The derived class needs to implement the fuelTankSize() method"; }; + + double range(double fuelTankFillLevel) + { return 100*fuelTankFillLevel*asImp_().fuelTankSize()/asImp_().gasUsage(); } + +protected: + // reinterpret 'this' as a pointer to an object of type 'Imp' + Imp &asImp_() { return *static_cast(this); } +}; +\end{lstlisting} +(Notice the \texttt{asImp\_()} calls in the \texttt{range} method.) The +derived classes may now be defined like this: +\begin{lstlisting}[name=staticcars,basicstyle=\ttfamily\scriptsize,numbers=left,numberstyle=\tiny, numbersep=5pt] +// A Mercedes S-class car +class S : public Car +{public: + double gasUsage() { return 9.0; }; + double fuelTankSize() { return 65.0; }; +}; + +// A VW Lupo +class Lupo : public Car +{public: + double gasUsage() { return 2.99; }; + double fuelTankSize() { return 30.0; }; +}; +\end{lstlisting} +\end{example} + +\noindent +Analogous to example \ref{example:DynPoly}, the two kinds of cars can +be used generically within (template) functions: +\begin{lstlisting}[name=staticcars,basicstyle=\ttfamily\scriptsize,numbers=left,numberstyle=\tiny, numbersep=5pt] +template +void printMaxRange(CarType &car) +{ std::cout << "Maximum Range: " << car.range(1.00) << "\n"; } + +int main() +{ + Lupo lupo; + S s; + std::cout << "VW Lupo:"; + std::cout << "Median range: " << lupo.range(0.50) << "\n"; + printMaxRange(lupo); + std::cout << "Mercedes S-Class:"; + std::cout << "Median range: " << s.range(0.50) << "\n"; + printMaxRange(s); + return 0; +} +\end{lstlisting} + +%\textbf{TODO: Exercise} + +\section{Common Template Programming Related Problems} + +Although C++ template programming opens a few intriguing +possibilities, it also has a few disadvantages. In this section, a few +of them are outlined and some hints on how they can be dealt with are +provided. + +\subsection*{Identifier-Name Blow-Up} + +One particular problem with advanced use of C++ templates is that the +canonical identifier names for types and methods quickly become really +long and unintelligible. For example, a typical error message +generated using GCC 4.5 and \Dune-PDELab looks like +\begin{lstlisting}[basicstyle=\ttfamily\scriptsize, numbersep=5pt] +test_pdelab.cc:171:9: error: no matching function for call to ‘Dune::\ +PDELab::GridOperatorSpace, (Dune::PartitionIteratorType)4u> >, Dune::\ +PDELab::Q1LocalFiniteElementMap, Dune::PDELab::\ +NoConstraints, Dumux::PDELab::BoxISTLVectorBackend >, 2, Dune::PDELab::GridFunctionSpaceBlockwiseMapper>\ +, Dune::PDELab::PowerGridFunctionSpace, \ +(Dune::PartitionIteratorType)4u> >, Dune::PDELab::Q1LocalFiniteElementMap\ +, Dune::PDELab::NoConstraints, Dumux::PDELab::\ +BoxISTLVectorBackend >, 2, Dune::\ +PDELab::GridFunctionSpaceBlockwiseMapper>, Dumux::PDELab::BoxLocalOperator\ +, Dune::PDELab::\ +ConstraintsTransformation, Dune::PDELab::\ +ConstraintsTransformation, Dune::PDELab::\ +ISTLBCRSMatrixBackend<2, 2>, true>::GridOperatorSpace()’ +\end{lstlisting} +This seriously complicates diagnostics. Although there is no full +solution for this problem yet, an effective way of dealing with such +kinds of error messages is to ignore the type information and to just +look at the location given at the beginning of the line. If nested +templates are used, the lines printed by the compiler above the actual +error message specify how exactly the code was instantiated (the lines +starting with ``\texttt{instantiated from}''). In this case it is +advisable to look at the innermost source code location of ``recently +added'' source code. + +\subsection*{Proliferation of Template Parameters} + +Templates often need a large number of template parameters. For +example, the error message above was produced by the following +snipplet: +\begin{lstlisting}[basicstyle=\ttfamily\scriptsize,numbers=left,numberstyle=\tiny, numbersep=5pt] +int main() +{ + enum {numEq = 2}; + enum {dim = 3}; + typedef Dune::UGGrid Grid; + typedef Grid::LeafGridView GridView; + typedef Dune::PDELab::Q1LocalFiniteElementMap FEM; + typedef TTAG(LensProblem) TypeTag; + typedef Dune::PDELab::NoConstraints Constraints; + typedef Dune::PDELab::GridFunctionSpace< + GridView, FEM, Constraints, Dumux::PDELab::BoxISTLVectorBackend + > + doubleGridFunctionSpace; + typedef Dune::PDELab::PowerGridFunctionSpace< + doubleGridFunctionSpace, + numEq, + Dune::PDELab::GridFunctionSpaceBlockwiseMapper + > + GridFunctionSpace; + typedef typename GridFunctionSpace::ConstraintsContainer::Type + ConstraintsTrafo; + typedef Dumux::PDELab::BoxLocalOperator LocalOperator; + typedef Dune::PDELab::GridOperatorSpace< + GridFunctionSpace, + GridFunctionSpace, + LocalOperator, + ConstraintsTrafo, + ConstraintsTrafo, + Dune::PDELab::ISTLBCRSMatrixBackend, + true + > + GOS; + GOS gos; // instantiate grid operator space +} +\end{lstlisting} + +Although the code above is not really intuitivly readable, this does +not pose a severe problem as long as the type (in this case the grid +operator space) needs to be specified exactly once in the whole +program. If, on the other hand, it needs to be consistend over +multiple locations in the source code, measures have to be taken in +order to keep the code maintainable. + +\section{Traits Classes} + +A classic approach to reduce the number of template parameters is to +gather the all arguments in a special class, a so-called traits +class. Instead of writing +\begin{lstlisting}[basicstyle=\ttfamily\scriptsize,numbers=left,numberstyle=\tiny, numbersep=5pt] +template +class MyClass {}; +\end{lstlisting} +one can use +\begin{lstlisting}[basicstyle=\ttfamily\scriptsize,numbers=left,numberstyle=\tiny, numbersep=5pt] +template +class MyClass {}; +\end{lstlisting} +where the \texttt{Traits} class contains public type definitions for +\texttt{A}, \texttt{B}, \texttt{C} and \texttt{D}, e.g. +\begin{lstlisting}[basicstyle=\ttfamily\scriptsize,numbers=left,numberstyle=\tiny, numbersep=5pt] +struct MyTraits +{ + typedef float A; + typedef double B; + typedef short C; + typedef int D; +}; +\end{lstlisting} + +\noindent +As there is no a free lunch, the traits approach comes with a few +disadvantages of its own: +\begin{enumerate} +\item Hierarchies of traits classes are problematic. This is due to + the fact that each level of the hierarchy must be self-contained. As + a result it is impossible to define parameters in the base class + which depend on parameters which only later get specified by a + derived traits class. +\item Traits quickly lead to circular dependencies. In practice + this means that traits classes can not extract any information from + templates which get the traits class as an argument -- even if the + extracted information does not require the traits class. +\end{enumerate} + +\noindent +To see the point of the first issue, consider the following: +\begin{lstlisting}[basicstyle=\ttfamily\scriptsize,numbers=left,numberstyle=\tiny, numbersep=5pt] +struct MyBaseTraits { + typedef int Scalar; + typedef std::vector Vector; + typedef std::list List; + typedef std::array Array; + typedef std::set Set; +}; + +struct MyDoubleTraits : public MyBaseTraits { + typedef double Scalar; +}; + +int main() { + MyDoubleTraits::Vector v{1.41421, 1.73205, 2}; + for (int i = 0; i < v.size(); ++i) + std::cout << v[i]*v[i] << std::endl; +} +\end{lstlisting} +Contrary to what is intended, \texttt{v} is a vector of integers. This +problem cannot also be solved using static polymorphism, since it +would lead to a cyclic dependency between \texttt{MyBaseTraits} and +\texttt{MyDoubleTraits}. + +The second issue is illuminated by the following example, where one +would expect the \texttt{MyTraits:: VectorType} to be \texttt{std::vector}: +\begin{lstlisting}[basicstyle=\ttfamily\scriptsize,numbers=left,numberstyle=\tiny, numbersep=5pt] +template +class MyClass { +public: typedef double ScalarType; +private: typedef typename Traits::VectorType VectorType; +}; + +struct MyTraits { + typedef MyClass::ScalarType ScalarType; + typedef std::vector VectorType +}; +\end{lstlisting} +Although this example seems to be quite pathetic, in practice it is +often useful to specify parameters in such a way. + +% TODO: section about separation of functions, parameters and +% independent variables. (e.g. the material laws: BrooksCorey +% (function), BrooksCoreyParams (function parameters), wetting +% saturation/fluid state (independent variables) + +%%% Local Variables: +%%% mode: latex +%%% TeX-master: "dumux-handbook" +%%% End: diff --git a/doc/handbook/dumux-handbook.tex b/doc/handbook/dumux-handbook.tex index 09a904969..372ea2776 100644 --- a/doc/handbook/dumux-handbook.tex +++ b/doc/handbook/dumux-handbook.tex @@ -109,6 +109,7 @@ Universit\"at Stuttgart, Paffenwaldring 61, D-70569 Stuttgart, Germany}\\ \input{intro} \input{getting-started} +\input{designpatterns} \input{propertysystem} \input{tutorial} \input{structure} diff --git a/doc/handbook/propertysystem.tex b/doc/handbook/propertysystem.tex index ad8b0fbfc..2eabeb8dc 100644 --- a/doc/handbook/propertysystem.tex +++ b/doc/handbook/propertysystem.tex @@ -1,479 +1,3 @@ -\chapter{Design Patterns} -\label{chap:DesignPatterns} - -This chapter tries to give a high-level understanding of some of the -fundamental techniques which are used by \Dune and \Dumux and the -motivation behind these design decisions. It is assumed that the -reader already has basic experience in object oriented programming -with C++. - -First, a quick motivation of C++ template programming is given. Then -follows an introduction to polymorphism and the opportunities for it -opened by the template mechanism. After that, some drawbacks -associated with template programming are discussed, in particular the -blow-up of identifier names and proliferation of template arguments -and some approaches on how to deal with these problems. - -\section{C++ Template Programming} - -One of the main features of modern versions of the C++ programming -language is robust support for templates. Templates are a mechanism -for code generation build directly into the compiler. For the -motivation behind templates, consider a linked list of \texttt{double} -values which could be implemented like this: -\begin{lstlisting}[basicstyle=\ttfamily\scriptsize,numbers=left,numberstyle=\tiny, numbersep=5pt] -struct DoubleList { - DoubleList(const double &val, DoubleList *prevNode = 0) - { value = val; if (prevNode) prevNode->next = this; }; - double value; - DoubleList *next; -}; -int main() { - DoubleList *head, *tail; - head = tail = new DoubleList(1.23); - tail = new DoubleList(2.34, tail); - tail = new DoubleList(3.56, tail); -}; -\end{lstlisting} -But what should be done if a list of strings is also required? The -only ``clean'' way to achive this without templates would be to copy -\texttt{DoubleList}, then rename it and change the type of the -\texttt{value} attribute. It is obvious that this is a very -cumbersome, error-prone and unproductive process. For this reason, -recent standards of the C++ programming language specify the template -mechanism, which is a way let the compiler do the tedious work. Using -templates, a generic linked list can be implemented like this: -\begin{lstlisting}[basicstyle=\ttfamily\scriptsize,numbers=left,numberstyle=\tiny, numbersep=5pt] -template -struct List { - List(const ValueType &val, List *prevNode = 0) - { value = val; if (prevNode) prevNode->next = this; }; - ValueType value; - List *next; -}; -int main() { - typedef List DoubleList; - DoubleList *head, *tail; - head = tail = new DoubleList(1.23); - tail = new DoubleList(2.34, tail); - tail = new DoubleList(3.56, tail); - - typedef List StringList; - StringList *head2, *tail2; - head2 = tail2 = new StringList("Hello"); - tail2 = new StringList(", ", tail2); - tail2 = new StringList("World!", tail2); -}; -\end{lstlisting} - -Compared to approaches which use external tools for code generation -- -which is the approach chosen for example by the -FEniCS~\cite{FENICS-HP} project -- or heavy (ab)use of the C -preprocessor -- as done for example by the UG framework~\cite{UG-HP} --- templates have several advantages: -\begin{description} -\item[Well Programmable:] Programming errors are directly detected by - the C++ compiler. Thus, diagnostic messages from the compiler are - directly useful because the source code which gets compiled is the - same as the one written by the developer. This is not the case - for code generators and C-preprocessor macros where the actual - statements processed by the compiler are obfuscated. -\item[Easily Debugable:] Programs which use the template mechanism can be - debugged almost as easily as C++ programs which do not use - templates. This is due to the fact that the debugger always knows - the ``real'' source file and line number. -\end{description} -For these reasons \Dune and \Dumux extensively use the template -mechanism. Both projects also try to avoid duplicating functionality -provided by the Standard Template Library (STL,~\cite{STL-REF-HP}) -which is part of the C++ standard and functionality provided by the -quasi-standard Boost~\cite{BOOST-HP} libraries. - -\section{Polymorphism} - -In object oriented programming, some methods often makes sense for all -classes in a hierarchy, but what actually needs to be \textit{done} -can differ for each concrete class. This observation motivates -\textit{polymorphism}. Fundamentally, polymorphism means all -techniques where a method call results in the processor executing code -which is specific to the type of object for which the method is -called\footnote{This is \textit{poly} of polymorphism: There are - multiple ways to achieve the same goal.}. - -In C++, there are two common ways to achieve polymorphism: The -traditional dynamic polymorphism which does not require template -programming, and static polymorphism which is made possible by the -template mechanism. - -\subsection*{Dynamic Polymorphism} - -To utilize \textit{dynamic polymorphism} in C++, the polymorphic -methods are marked with the \texttt{virtual} keyword in the base -class. Internally, the compiler realizes dynamic polymorphism by -storing a pointer to a so-called \texttt{vtable} within each object of -polymorphic classes. The \texttt{vtable} itself stores the entry point -of each method which is declared \texttt{virtual}. If such a method is -called on an object, the compiler generates code which retrieves the -method's memory address from the object's \texttt{vtable} and then -continues execution at this address. This explains why this mechanism -is called \textbf{dynamic} polymorphism: the code which is actually -executed is dynamically determined at run time. - -\begin{example} - \label{example:DynPoly} - A class called \texttt{Car} could feature the methods - \texttt{gasUsage}, which by default corrosponds to the current - $CO_2$ emission goal of the European Union but can be changed by - classes representing actual cars. Also, a method called - \texttt{fuelTankSize} makes sense for all cars, but since there is - no useful default, its \texttt{vtable} entry is set to $0$ in the - base class. This tells the compiler that it is mandatorily that this - method gets defined in derived classes. Finally, the method - \texttt{range} may calculate the expected remaining kilometers the - car can drive given a fill level of the fuel tank. Since the - \texttt{range} method can retrieve information it needs, it does not - need to be polymorphic. -\begin{lstlisting}[basicstyle=\ttfamily\scriptsize,numbers=left,numberstyle=\tiny, numbersep=5pt] -// The base class -class Car -{public: - virtual double gasUsage() - { return 4.5; }; - virtual double fuelTankSize() = 0; - - double range(double fuelTankFillLevel) - { return 100*fuelTankFillLevel*fuelTankSize()/gasUsage(); } -}; -\end{lstlisting} - -\noindent -Actual car models can now be derived from the base class like this: -\begin{lstlisting}[basicstyle=\ttfamily\scriptsize,numbers=left,numberstyle=\tiny, numbersep=5pt] -// A Mercedes S-class car -class S : public Car -{public: - virtual double gasUsage() { return 9.0; }; - virtual double fuelTankSize() { return 65.0; }; -}; - -// A VW Lupo -class Lupo : public Car -{public: - virtual double gasUsage() { return 2.99; }; - virtual double fuelTankSize() { return 30.0; }; -}; -\end{lstlisting} - -\noindent -Calling the \texttt{range} method yields the correct result for any -kind of car: -\begin{lstlisting}[basicstyle=\ttfamily\scriptsize,numbers=left,numberstyle=\tiny, numbersep=5pt] -void printMaxRange(Car &car) -{ std::cout << "Maximum Range: " << car.range(1.00) << "\n"; } - -int main() -{ - Lupo lupo; - S s; - std::cout << "VW Lupo:"; - std::cout << "Median range: " << lupo.range(0.50) << "\n"; - printMaxRange(lupo); - std::cout << "Mercedes S-Class:"; - std::cout << "Median range: " << s.range(0.50) << "\n"; - printMaxRange(s); -} -\end{lstlisting} - -For both types of cars, \texttt{Lupo} and \texttt{S} the -\texttt{printMaxRange} function works as expected, yielding -$1003.3\;\mathrm{km}$ for the Lupo and $722.2\;\mathrm{km}$ for the -S-Class. -\end{example} - -\begin{exc} -What happens if \dots -\begin{itemize} -\item \dots the \texttt{gasUsage} method is removed from the \texttt{Lupo} class? -\item \dots the \texttt{virtual} qualifier is removed in front of the - \texttt{gasUsage} method in the base class? -\item \dots the \texttt{fuelTankSize} method is removed from the \texttt{Lupo} class? -\item \dots the \texttt{range} method in the \texttt{S} class is - overwritten? -\end{itemize} -\end{exc} - -\subsection*{Static Polymorphism} - -Dynamic polymorphism has a few disadvantages. The most relevant in the -context of \Dumux is that the compiler can not see ``inside'' the -called methods and thus cannot optimize properly. For example, modern -C++ compilers 'inline' short methods, i.e. they copy the method's body -to where it is called. First, inlining allows to save a few -instructions by avoiding to jump into and out of the method. Second, -and more importantly, inlining also allows further optimizations which -depend on specific properties of the function arguments (e.g. constant -value elimination) or the contents of the function body (e.g. loop -unrolling). Unfortunately, inlining and other cross-method -optimizations are made next to impossible by dynamic -polymorphism. This is because these optimizations are done by the -compiler (i.e. at compile time) whilst the code which actually gets -executed is only determined at run time for \texttt{virtual} -methods. To overcome this issue, template programming can be used to -achive polymorphism at compile time. This works by supplying the type -of the derived class as an additional template parameter to the base -class. Whenever the base class needs to call back the derived class, -the \texttt{this} pointer of the base class is reinterpreted as a -being a pointer to an object of the derived class and the method is -then called on the reinterpreted pointer. This scheme gives the C++ -compiler complete transparency of the code executed and thus opens -much better optimization oportunities. Since this mechanism completely -happens at compile time, it is called ``static polymorphism'' because -the called method cannot be dynamically changed at runtime. -\begin{example} - Using static polymorphism, the base class of example \ref{example:DynPoly} - can be implemented like: -\begin{lstlisting}[name=staticcars,basicstyle=\ttfamily\scriptsize,numbers=left,numberstyle=\tiny, numbersep=5pt] -// The base class. The 'Imp' template parameter is the -// type of the implementation, i.e. the derived class -template -class Car -{public: - double gasUsage() - { return 4.5; }; - double fuelTankSize() - { throw "The derived class needs to implement the fuelTankSize() method"; }; - - double range(double fuelTankFillLevel) - { return 100*fuelTankFillLevel*asImp_().fuelTankSize()/asImp_().gasUsage(); } - -protected: - // reinterpret 'this' as a pointer to an object of type 'Imp' - Imp &asImp_() { return *static_cast(this); } -}; -\end{lstlisting} -(Notice the \texttt{asImp\_()} calls in the \texttt{range} method.) The -derived classes may now be defined like this: -\begin{lstlisting}[name=staticcars,basicstyle=\ttfamily\scriptsize,numbers=left,numberstyle=\tiny, numbersep=5pt] -// A Mercedes S-class car -class S : public Car -{public: - double gasUsage() { return 9.0; }; - double fuelTankSize() { return 65.0; }; -}; - -// A VW Lupo -class Lupo : public Car -{public: - double gasUsage() { return 2.99; }; - double fuelTankSize() { return 30.0; }; -}; -\end{lstlisting} -\end{example} - -\noindent -Analogous to example \ref{example:DynPoly}, the two kinds of cars can -be used generically within (template) functions: -\begin{lstlisting}[name=staticcars,basicstyle=\ttfamily\scriptsize,numbers=left,numberstyle=\tiny, numbersep=5pt] -template -void printMaxRange(CarType &car) -{ std::cout << "Maximum Range: " << car.range(1.00) << "\n"; } - -int main() -{ - Lupo lupo; - S s; - std::cout << "VW Lupo:"; - std::cout << "Median range: " << lupo.range(0.50) << "\n"; - printMaxRange(lupo); - std::cout << "Mercedes S-Class:"; - std::cout << "Median range: " << s.range(0.50) << "\n"; - printMaxRange(s); - return 0; -} -\end{lstlisting} - -%\textbf{TODO: Exercise} - -\section{Common Template Programming Related Problems} - -Although C++ template programming opens a few intriguing -possibilities, it also has a few disadvantages. In this section, a few -of them are outlined and some hints on how they can be dealt with are -provided. - -\subsection*{Identifier-Name Blow-Up} - -One particular problem with advanced use of C++ templates is that the -canonical identifier names for types and methods quickly become really -long and unintelligible. For example, a typical error message -generated using GCC 4.5 and \Dune-PDELab looks like -\begin{lstlisting}[basicstyle=\ttfamily\scriptsize, numbersep=5pt] -test_pdelab.cc:171:9: error: no matching function for call to ‘Dune::\ -PDELab::GridOperatorSpace, (Dune::PartitionIteratorType)4u> >, Dune::\ -PDELab::Q1LocalFiniteElementMap, Dune::PDELab::\ -NoConstraints, Dumux::PDELab::BoxISTLVectorBackend >, 2, Dune::PDELab::GridFunctionSpaceBlockwiseMapper>\ -, Dune::PDELab::PowerGridFunctionSpace, \ -(Dune::PartitionIteratorType)4u> >, Dune::PDELab::Q1LocalFiniteElementMap\ -, Dune::PDELab::NoConstraints, Dumux::PDELab::\ -BoxISTLVectorBackend >, 2, Dune::\ -PDELab::GridFunctionSpaceBlockwiseMapper>, Dumux::PDELab::BoxLocalOperator\ -, Dune::PDELab::\ -ConstraintsTransformation, Dune::PDELab::\ -ConstraintsTransformation, Dune::PDELab::\ -ISTLBCRSMatrixBackend<2, 2>, true>::GridOperatorSpace()’ -\end{lstlisting} -This seriously complicates diagnostics. Although there is no full -solution for this problem yet, an effective way of dealing with such -kinds of error messages is to ignore the type information and to just -look at the location given at the beginning of the line. If nested -templates are used, the lines printed by the compiler above the actual -error message specify how exactly the code was instantiated (the lines -starting with ``\texttt{instantiated from}''). In this case it is -advisable to look at the innermost source code location of ``recently -added'' source code. - -\subsection*{Proliferation of Template Parameters} - -Templates often need a large number of template parameters. For -example, the error message above was produced by the following -snipplet: -\begin{lstlisting}[basicstyle=\ttfamily\scriptsize,numbers=left,numberstyle=\tiny, numbersep=5pt] -int main() -{ - enum {numEq = 2}; - enum {dim = 3}; - typedef Dune::UGGrid Grid; - typedef Grid::LeafGridView GridView; - typedef Dune::PDELab::Q1LocalFiniteElementMap FEM; - typedef TTAG(LensProblem) TypeTag; - typedef Dune::PDELab::NoConstraints Constraints; - typedef Dune::PDELab::GridFunctionSpace< - GridView, FEM, Constraints, Dumux::PDELab::BoxISTLVectorBackend - > - doubleGridFunctionSpace; - typedef Dune::PDELab::PowerGridFunctionSpace< - doubleGridFunctionSpace, - numEq, - Dune::PDELab::GridFunctionSpaceBlockwiseMapper - > - GridFunctionSpace; - typedef typename GridFunctionSpace::ConstraintsContainer::Type - ConstraintsTrafo; - typedef Dumux::PDELab::BoxLocalOperator LocalOperator; - typedef Dune::PDELab::GridOperatorSpace< - GridFunctionSpace, - GridFunctionSpace, - LocalOperator, - ConstraintsTrafo, - ConstraintsTrafo, - Dune::PDELab::ISTLBCRSMatrixBackend, - true - > - GOS; - GOS gos; // instantiate grid operator space -} -\end{lstlisting} - -Although the code above is not really intuitivly readable, this does -not pose a severe problem as long as the type (in this case the grid -operator space) needs to be specified exactly once in the whole -program. If, on the other hand, it needs to be consistend over -multiple locations in the source code, measures have to be taken in -order to keep the code maintainable. - -\section{Traits Classes} - -A classic approach to reduce the number of template parameters is to -gather the all arguments in a special class, a so-called traits -class. Instead of writing -\begin{lstlisting}[basicstyle=\ttfamily\scriptsize,numbers=left,numberstyle=\tiny, numbersep=5pt] -template -class MyClass {}; -\end{lstlisting} -one can use -\begin{lstlisting}[basicstyle=\ttfamily\scriptsize,numbers=left,numberstyle=\tiny, numbersep=5pt] -template -class MyClass {}; -\end{lstlisting} -where the \texttt{Traits} class contains public type definitions for -\texttt{A}, \texttt{B}, \texttt{C} and \texttt{D}, e.g. -\begin{lstlisting}[basicstyle=\ttfamily\scriptsize,numbers=left,numberstyle=\tiny, numbersep=5pt] -struct MyTraits -{ - typedef float A; - typedef double B; - typedef short C; - typedef int D; -}; -\end{lstlisting} - -\noindent -As there is no a free lunch, the traits approach comes with a few -disadvantages of its own: -\begin{enumerate} -\item Hierarchies of traits classes are problematic. This is due to - the fact that each level of the hierarchy must be self-contained. As - a result it is impossible to define parameters in the base class - which depend on parameters which only later get specified by a - derived traits class. -\item Traits quickly lead to circular dependencies. In practice - this means that traits classes can not extract any information from - templates which get the traits class as an argument -- even if the - extracted information does not require the traits class. -\end{enumerate} - -\noindent -To see the point of the first issue, consider the following: -\begin{lstlisting}[basicstyle=\ttfamily\scriptsize,numbers=left,numberstyle=\tiny, numbersep=5pt] -struct MyBaseTraits { - typedef int Scalar; - typedef std::vector Vector; - typedef std::list List; - typedef std::array Array; - typedef std::set Set; -}; - -struct MyDoubleTraits : public MyBaseTraits { - typedef double Scalar; -}; - -int main() { - MyDoubleTraits::Vector v{1.41421, 1.73205, 2}; - for (int i = 0; i < v.size(); ++i) - std::cout << v[i]*v[i] << std::endl; -} -\end{lstlisting} -Contrary to what is intended, \texttt{v} is a vector of integers. This -problem cannot also be solved using static polymorphism, since it -would lead to a cyclic dependency between \texttt{MyBaseTraits} and -\texttt{MyDoubleTraits}. - -The second issue is illuminated by the following example, where one -would expect the \texttt{MyTraits:: VectorType} to be \texttt{std::vector}: -\begin{lstlisting}[basicstyle=\ttfamily\scriptsize,numbers=left,numberstyle=\tiny, numbersep=5pt] -template -class MyClass { -public: typedef double ScalarType; -private: typedef typename Traits::VectorType VectorType; -}; - -struct MyTraits { - typedef MyClass::ScalarType ScalarType; - typedef std::vector VectorType -}; -\end{lstlisting} -Although this example seems to be quite pathetic, in practice it is -often useful to specify parameters in such a way. - -% TODO: section about separation of functions, parameters and -% independent variables. (e.g. the material laws: BrooksCorey -% (function), BrooksCoreyParams (function parameters), wetting -% saturation/fluid state (independent variables) - \chapter{The \Dumux Property System} \label{sec:propertysystem}