class: center, middle # Session 13 ## `C++11` - and overview ### How to use the new language features in AMDiS --- # Agenda ### Friday - **Using `C++11` with AMDiS** - Extensions - Software Development and Workflow (Ansgar Burchard) - AMDiS User-Group meeting - Presentation of Student Projects (Some notes are taken from presentation of Rainer Grimm) --- class: center, middle # Bjarne Stroustrup about `C++11` *„Surprisingly, `C++11` feels like a new language - the pieces just fit together better.“* --- # History - `C++98`: first ISO standard - `C++03`: technical corrigendum - TR1: technical report 1 - `C++11`: accepted in August 2011 - `C++14`: current ISO standard - `C++1z`: future ISO standard (maybe 2017) --- # Design goals of `C++11` `C++` has from its inception been a general-purpose programming language with a bias towards systems programming that - is a better C - supports data abstraction - supports object-oriented programming - supports generic programming ### The overall aims for `C++11`: - Make `C++` a better language for systems programming and library building - Make `C++` easier to teach and learn - Very stringent compatibility constraints. ### General principles of `C++` - Trust the programmer. - You don't have to pay for something you don't need. - Don't break existing code. - Prefer compile time errors over run time errors. --- # auto For variables, specifies that the type of the variable that is being declared will be automatically deduced from its initializer. For functions, specifies that the return type is a trailing return type. ### Examples ``` auto x = 1.0 + f(2.0); auto iter = vector.begin(); // Iterator auto expr = valueOf(u) + X(0); // AMDiS-Expression ``` ### Deduction rules: When auto sets the type of a declared variable from its initializing expression, it proceeds as follows: 1. If the initializing expression is a reference, the **reference is ignored**. 2. If, after Step 1 has been performed, there is a **top-level const** and/or volatile qualifier, it **is ignored**. --- # decltype `decltype( expr )` inspects the declared type of an entity or the type and value category of an expression. ### Examples: ``` int x; const int cx = 42; const int& crx = x; typedef decltype(x) x_type; // int auto a = x; // int typedef decltype(cx) cx_type; // const int auto b = cx; // int typedef decltype(crx) crx_type; // int const& auto c = crx; // int ``` 1. If `expr` is a is a plain, unparenthesized variable, function parameter, or class member access, then `decltype(expr)` is the type of that variable, function parameter, or class member as declared in the source code. 2. Let `expr` be an expression that is not as in 1. Let `T` be the type of `expr`. If `expr` is an *lvalue*, then `decltype(expr)` is `T&`. If `expr` is an *xvalue*, then `decltype(expr)` is `T&&`. Otherwise, `expr` is a *prvalue*, and `ecltype(expr)` is `T`. --- # auto + decltype Use a combination of both for functions with trailing return type: ``` template
auto f(T1 a, T2 b) -> decltype( a*b ); ``` where the return-type of the function is deduced from the expression involving `a` and `b`. ### Can be used to declare functions returning an AMDiS expression. ``` struct A { DOFVector
& u; template
auto add(E expr) -> decltype( valueOf(this->u) + expr ) { return valueOf(u) + expr; } }; ``` (See [C++ auto and decltype Explained](http://goo.gl/9KxeDP) for details about `auto` and `decltype` specifier. Some of the statements are taken from there.) --- # Lambda expression Lets you define functions locally, at the place of the call, and has the form: ``` [capture](parameters) -> return_type { body } ``` ### Example: Dirichlet condition ``` prob.addDirichletBC(boundaryNr, 0, 0, [](WorldVector
const& x) { return std::sin(x[0]); }); ``` -- - Use capture list can be used to include variables from the sourrounding scope to the function scope: ``` int var = 1; auto f = [var](double x) { return var*x; }; ``` - Return type can be ommited, if only one return statement in function body. - lambda expression can be stored in variables, using `auto` type deduction - lambda expression can be assigned to `std::function`: ``` std::function
f = [](double x) { return x + 1.0; }; ``` --- # Lambda expressions Wrap a lambda expression to use it in place of an arbitrary `AbstractFunction`s ``` template
class LambdaFunction : public AbstractFunction
{ public: template
LambdaFunction(F&& fct) : fct(std::forward
(fct)) {} virtual ReturnType operator()(ArgumentTypes const&... args) const override { return fct(args...); } private: std::function
fct; }; ``` Usage: ``` DOFVector
u = ...; u.interpol(new LambdaFunction
>( [](WorldVector
const& x) { return std::sin(x[0]); }) ); ``` (See *variadic templates* for details about the handling of the template arguments) --- # Range based for loops The range-based for loop has the following form: ``` for ( declaration : expression ) statement ``` with `declaraion` a statement to declare a variable thar represents the elements of a container or list `expression`. The `statement` can contain the declared variable. ### Example: ``` int array[5] = { 1, 2, 3, 4, 5 }; for (int& x : array) x *= 2; ``` or simply ``` for (int x : { 1, 2, 3, 4, 5 }) std::cout << x << "\n"; ``` --- # Range based for loops ### Using range based loops with AMDiS iterators. A range-based loop is internally translated to ``` { auto&& __range = expression; for (auto __begin = begin_expr, __end = end_expr; __begin != __end; ++__begin) { declaration = *__begin; statement } } ``` where `begin_expr` and `end_expre` are either ``` __range.begin() __range.end() // if found as member, otherwise begin(__range) end(__range) // found by argument dependend lookup ``` Thus, the containers we want to iterate over (e.g. DOFVector, Mesh) must provide these `begin(), end()` method. - `DOFVector` has `begin()` and `end()` that point to the unterlying `std::vector` --- # Range based for loops ### Provide a DOFIterator wrapper ``` template
class DofRange { using T = typename Container::value_type; template
struct Iterator { /* ... */ }; using ConstIter = Iterator
const, DOFConstIterator
>; struct MutableIter : Iterator
, DOFIterator
> { /* ... */ }; public: DofRange(Container& vec, DOFIteratorType flag) : vec(vec), flag(flag) {} std::conditional_t
::value, ConstIter, MutableIter> begin() const { return {vec, flag, false}; } std::conditional_t
::value, ConstIter, MutableIter> end() const { return {vec, flag, true}; } private: Container& vec; DOFIteratorType flag; }; ``` --- # Range based for loops ### Provide a DOFIterator wrapper Usage: ``` template
DofRange
used_dofs(Vector& vec) { return {vec, USED_DOFS}; } // ... DOFVector
& u = *prob.getSolution(0); int i = 0; for(auto& x : used_dofs(u)) x = i++; ``` (Details can be found in `extensions/cpp11/DofRange.hpp`) --- # Range based for loops ### Provide a Mesh Traversal wrapper ``` class MeshTraversal { struct Iterator; public: MeshTraversal(Mesh* mesh, Flag flags) : mesh(mesh) , flags(flags) {} Iterator begin() const { return {mesh, flags, false}; } Iterator end() const { return {mesh, flags, true}; } private: Mesh* mesh; Flag flags; }; ``` --- # Range based for loops with `MeshTraversal::Iterator` defined by ``` struct Iterator { Iterator(Mesh* mesh, Flag flags, bool end = false) : end(end) { if (!end) { stack.reset(new TraverseStack); elInfo = stack->traverseFirst(mesh, -1, flags); } } Iterator(Iterator&&) = default; ElInfo const* operator*() const { return elInfo; } ElInfo* operator*() { return elInfo; } bool operator==(Iterator const& other) const; bool operator!=(Iterator const& rhs) const; Iterator& operator++() { elInfo = stack->traverseNext(elInfo); return *this; } protected: std::unique_ptr
stack; ElInfo* elInfo = nullptr; bool end; }; ``` --- # Range based for loops ### Provide a Mesh Traversal wrapper Usage: ``` MeshTraversal traverse(Mesh* mesh, Flag flags = 0) { return {mesh, Mesh::CALL_LEAF_EL | flags}; } // ... Mesh* mesh = prob.getMesh(); for (ElInfo* elInfo : traverse(mesh)) { std::cout << elInfo->getElement()->getIndex() << "\n"; } ``` (Details can be found in `extensions/cpp11/MeshTraversal.hpp`) --- # Variadic templates A template parameter pack is a template parameter that accepts **zero or more template arguments** (non-types, types, or templates). A function parameter pack is a function parameter that accepts **zero or more function arguments**. A template with at least one parameter pack is called a *variadic template*. ``` template
struct Tuple { /* ... */ }; template
??? min(Args const&... args) { /* ... */ } template
struct IntSeq; ``` The three dots `...` induce an expansion of the parameter pack (pattern) directly before the three dots to a list of arguments. - An individual type (argument) can not directly be accessed. - Work with variadic templates is based on recursion - Break condition is based on fixed number of arguments (or empty list) - No runtime-overhead due to variadic argument list --- # Variadic templates ### Example: Access to tuple element. ``` template
Get; template
Get
> : Get
> {}; template
Get<0, T0> { using type = T0; }; ``` ### Example: minimum of many arguments ``` template
auto min(Arg0 const& a, Arg1 const& b) -> decltype( a < b ? a : b ) { return a < b ? a : b; } template
auto min(Arg0 const& a, Args const&... args) -> decltype( min(a, min(args...)) ) { return min(a, min(args...)); } ``` --- # Variadic templates ## AMDiS examples `1.` functor expression with arbitrary number of arguments ``` template
typename result_of::FunctionN
::type func(F const& f, Terms... ts) { /* ... */ } ``` Usage: ``` DOFVector
u = ...; u << func([](Arg1 arg1, Arg2 arg2 /*...*/) { /*...*/ }, expr1, expr2 /* ... */); ``` where `Arg1` is the `value_type` of `expr1`, and so on. -- **Remark:** The functor expression implements `LazyOperatorTerms`: ``` template
class LazyOperatorTerms : public LazyOperatorTermBase { std::tuple
term_tuple; }; ``` --- # Variadic templates ## AMDiS examples `2.` Abstract functions with many arguments ``` template
class AbstractFunction { public: /* ... */ virtual ReturnType operator()(Args const&... args) const = 0; }; ``` `3.` A `CouplingBaseProblem` for different `BaseProblem` types: ``` template
class CouplingBaseProblem : public CouplingIterationInterface, public CouplingTimeInterface, public AMDiS::detail::CouplingProblemStat
{ /*...*/ }; ``` `4.` AMDiS requires the constant `HAS_VARIADIC_TEMPLATES` to be set to 1 (is automatically set for the most of the compilers in `Config.h`).