Concepts of programming languages C++ Template Metaprogramming - - PowerPoint PPT Presentation

concepts of programming languages
SMART_READER_LITE
LIVE PREVIEW

Concepts of programming languages C++ Template Metaprogramming - - PowerPoint PPT Presentation

[Faculty of Science Information and Computing Sciences] Concepts of programming languages C++ Template Metaprogramming Michiel Magendans Robin Ganpat Jelmer van Nuss Hugo Heemskerk Thomas Dirkse 1 [Faculty of Science Information and


slide-1
SLIDE 1

[Faculty of Science Information and Computing Sciences] 1

Concepts of programming languages

C++ Template Metaprogramming

Michiel Magendans Robin Ganpat Jelmer van Nuss Hugo Heemskerk Thomas Dirkse

slide-2
SLIDE 2

[Faculty of Science Information and Computing Sciences] 2

Metaprogramming

Why program metaprograms?

◮ Programs writing programs, we’ve seen this before ◮ An introduction, not limited to C++ ◮ Very useful in the real world! ◮ Can go horribly wrong..

slide-3
SLIDE 3

[Faculty of Science Information and Computing Sciences] 3

Hypothetical company

◮ We make administration software! Exciting! ◮ Web application that allows us to interact with a

database

◮ Some business logic when interacting with the database

slide-4
SLIDE 4

[Faculty of Science Information and Computing Sciences] 4

What do we want to make?

◮ CRUD (Create, Read, Update, Delete) software requires

insert, update, etc. functions/statements

◮ A database table is essentially a small set of named

parameters, which themselves can have parameters

◮ Boilerplate heaven! So much code to write! ◮ Functions, getters, setters, loving it ◮ Always write the general case, don’t abstract

slide-5
SLIDE 5

[Faculty of Science Information and Computing Sciences] 5

How to monetize this

◮ Charge customer by programmer hour ◮ Hire lots more programmers ◮ Outsource ◮ Profjt!

slide-6
SLIDE 6

[Faculty of Science Information and Computing Sciences] 6

Lots of work though

◮ Work is getting cumbersome, let’s try

metaprogramming

Figure 1:

slide-7
SLIDE 7

[Faculty of Science Information and Computing Sciences] 7

Let’s reduce our workload

◮ Idea: Write a code generator! It writes the code for us! ◮ Set of 12 database table parameters to create 2k LoC,

spread out over 16 fjles

◮ Stringly typed, some extra xml, no documentation

necessary

◮ Manually add every generated fjle to a complex fjle tree

slide-8
SLIDE 8

[Faculty of Science Information and Computing Sciences] 8

More profjt!

◮ We have now guaranteed ourselves work for years to

come!

◮ When the generator changes, we will have to

retroactively update all previously generated fjles

◮ A job that would normally cost 30 minutes will now cost

10 hours at the minimum: we will make so much money!

◮ Technical debt? Owed by our customers, to us? Brilliant!

slide-9
SLIDE 9

[Faculty of Science Information and Computing Sciences] 9

Derp

Figure 2:

slide-10
SLIDE 10

[Faculty of Science Information and Computing Sciences] 10

But.. why?

◮ This is not due to incompetence! ◮ It’s a set of common business problems coming

together in an unfortunate way

◮ Strategy problems: no short-term fjnancial incentives to

work effjciently, historical artifacts

◮ People problems: Unwillingness to change, attachment

to own code, egos

◮ People problems are usually caused by strategy

problems

slide-11
SLIDE 11

[Faculty of Science Information and Computing Sciences] 11

What should happen in the business

◮ Difgerent business model can make a huge difgerence! ◮ Upfront single-price purchase ◮ SaaS (Software as a Service, subscription fee) ◮ Difgerent languages can prevent difgerent types of

situations from happening in the fjrst place

slide-12
SLIDE 12

[Faculty of Science Information and Computing Sciences] 12

What should happen in the tech

◮ DRY: Don’t Repeat Yourself ◮ Make the generic case trivial ◮ Extensibility: allow boilerplate to be written ifg

necessary, and let it override very specifjc parts of the meta program

◮ Never let programmers touch the generated code - it

might as well not exist

◮ Allow business logic to interact with metaprogrammed

classes as if they were written manually

◮ IDE/text editor support, humans are not good at

spotting syntax errors

slide-13
SLIDE 13

[Faculty of Science Information and Computing Sciences] 13

In OOP?

◮ You can do this in pure OOP too! ◮ However: requires multifaceted investment in

framework

◮ Business logic becomes harder to separate from the

framework

◮ Onboarding/training new developers becomes harder ◮ Language/IDEs/autocompletion might not be

cooperative

slide-14
SLIDE 14

[Faculty of Science Information and Computing Sciences] 14

There are costs and obstacles

◮ If your chosen language does not support any types of

metaprogramming, expect heavy costs, or rather don’t try at all

◮ Requires a higher upfront investment regardless ◮ Can be tougher to debug ◮ Small changes in the meta program afgect everything

slide-15
SLIDE 15

[Faculty of Science Information and Computing Sciences] 15

Takeaway

◮ Metaprogramming can be extremely useful and

extremely damaging if done wrong

◮ Choice in language and programming practices greatly

afgects how a business is, and can be run

◮ The opposite is also true! ◮ The question you need to ask yourself when choosing a

language is: What mistakes will I allow the company’s programmers to make? What bugs are worth their time?

slide-16
SLIDE 16

[Faculty of Science Information and Computing Sciences] 16

C++ Templates

What are they?

◮ A feature of C++ which allows functions and classes to

  • perate with generic types.

◮ Generics are generic until the types are substituted for

them at runtime. Templates are specialized at compile time.

◮ Templates allow explicit specialization. ◮ Type substition is done during compilation, not

run-time.

◮ There are Function Templates and Class Templates

slide-17
SLIDE 17

[Faculty of Science Information and Computing Sciences] 17

Function Templates - A Simple example

A possible Max()-function defjned in C++: int Max(int a, int b) { return a > b ? a : b; } Result: cout << Max(23, 40); #> 40

slide-18
SLIDE 18

[Faculty of Science Information and Computing Sciences] 18

A Simple example

The same function as previous slide, but for the double data type. double Max(double a, double b) { return a > b ? a : b; } Result: cout << Max(12.5, 48.21); #> 48.21

slide-19
SLIDE 19

[Faculty of Science Information and Computing Sciences] 19

So what’s the difgerence?

It is exactly the same logic, but the data type is difgerence.

This is where Templates come in handy!

slide-20
SLIDE 20

[Faculty of Science Information and Computing Sciences] 20

The Max()-function using Function Templates

template <typename T> T GetMax(T a, T b) { return a > b ? a : b; } Result: cout << Max(23, 40); #> 40 This invokes the template function with T == int. The function returns a value of int type.

slide-21
SLIDE 21

[Faculty of Science Information and Computing Sciences] 21

Class templates

◮ Function templates allow writing generic functions that

will work on many types.

◮ The same idea applies for class templates.

slide-22
SLIDE 22

[Faculty of Science Information and Computing Sciences] 22

A Simple example (1)

template <typename T> class ValueComparer { T first, second; public: ValueComparer(T a, T b) { first = a; second = b; } T getBiggerOne(); };

slide-23
SLIDE 23

[Faculty of Science Information and Computing Sciences] 23

A Simple example (2)

getBiggerOne() is a member function of the class template. Here is the implementation: template <typename T> T ValueComparer<T>::getBiggerOne() { return first > second ? first : second; } Result: ValueComparer <int> vc_int(69, 34); ValueComparer <double> vc_double(29.23, 49.133); cout << vc_int.getBiggerOne() << endl; cout << vc_double.getBiggerOne(); #> 69 #> 49.133

slide-24
SLIDE 24

[Faculty of Science Information and Computing Sciences] 24

Template Specialization

In some cases you may want a specifjc implementation for a specifjc data type.

This is where Template Specialization comes in.

slide-25
SLIDE 25

[Faculty of Science Information and Computing Sciences] 25

Template Specialization

ValueComparer Template defjnition from previous slide: template <typename T> class ValueComparer { T first, second; public: ValueComparer(T a, T b) { first = a; second = b; } T getBiggerOne(); }

slide-26
SLIDE 26

[Faculty of Science Information and Computing Sciences] 26

Lets say we want to create a specifjc implementation for the data type: string. The function getBiggerOne() should return the string with the biggest length. template <typename T> T ValueComparer<T>::getBiggerOne() { return first > second ? first : second; } template <> string ValueComparer<string>::getBiggerOne() { cout << "We can do even more things here." << endl; return first.length() > second.length() ? first : second; }

slide-27
SLIDE 27

[Faculty of Science Information and Computing Sciences] 27

The results

ValueComparer <int> vc_int(69, 34); ValueComparer <double> vc_double(29.23, 49.133) ValueComparer <string> vc_string("aaa", "bb"); std::cout << vc_int.getBiggerOne() << endl; std::cout << vc_double.getBiggerOne(); std::cout << vc_string.getBiggerOne(); #> 69 #> 49.133 #> We can do even more things here. #> aaa

slide-28
SLIDE 28

[Faculty of Science Information and Computing Sciences] 28

Generic Programming

◮ Polymorphism ◮ Widely used concept in OO languages ◮ Used to associate difgerent specifjc behaviours with a

single generic notation

slide-29
SLIDE 29

[Faculty of Science Information and Computing Sciences] 29

◮ In C++, you make use of inheritance and virtual

functions

Figure 3: C++ Polymorphism

slide-30
SLIDE 30

[Faculty of Science Information and Computing Sciences] 30

class GeoObj { public: virtual void draw() const = 0; virtual Coordinate center_of_gravity() const = 0; }; class Circle : public GeoObj { public: // Both methods implementations in .cpp file. virtual void draw() const; virtual Coordinate center_of_gravity() const; }

slide-31
SLIDE 31

[Faculty of Science Information and Computing Sciences] 31

◮ After creating the concrete objects, access the difgerent

concrete implementations using references or pointers to base class. // Draw any GeoObj void draw_shape(GeoObj const& shape) { shape.draw(); }

slide-32
SLIDE 32

[Faculty of Science Information and Computing Sciences] 32

// draw a collection of all kinds of shapes void draw_shapes(std::vector<GeoObj*> const& elems) { for(unsigned int i = 0; i < elems.size(); ++i) elems[i]->draw(); } // calculate the distance using the centers // of the shapes Coordinate distance_between(GeoObj const& x1, GeoObj const& x2) { return x1.center_of_gravity()

  • x2.center_of_gravity;

}

slide-33
SLIDE 33

[Faculty of Science Information and Computing Sciences] 33

Dynamic Polymorphism

◮ Compiler does not know at compile time which version

  • f the virtual function “draw” is used

◮ At runtime the right “draw” function is invoked

dynamically using the virtual functions

◮ This is called Dynamic Polymorphism

slide-34
SLIDE 34

[Faculty of Science Information and Computing Sciences] 34

Static Polymorphism

◮ Using templates, static polymorphism can be used. ◮ Relies on common syntax, rather than common

behaviour in base class.

slide-35
SLIDE 35

[Faculty of Science Information and Computing Sciences] 35

Figure 4: Dynamic C++ Polymorphism

slide-36
SLIDE 36

[Faculty of Science Information and Computing Sciences] 36

◮ No longer relies on base-class

// Concrete circle class, NOT derived from any class class Circle { public: // Both methods implementations in .cpp file. void draw() const; Coordinate center_of_gravity() const; }

Figure 5: Static C++ Polymorphism

slide-37
SLIDE 37

[Faculty of Science Information and Computing Sciences] 37

◮ Now using a template parameter

// Draw any GeoObj void draw_shape(GeoObj const& shape) { shape.draw(); }

slide-38
SLIDE 38

[Faculty of Science Information and Computing Sciences] 38

◮ Now using a template parameter

// Draw any GeoObj template <typename GeoObj> void draw_shape(GeoObj const& shape) { shape.draw(); }

slide-39
SLIDE 39

[Faculty of Science Information and Computing Sciences] 39

◮ Now using a template parameter ◮ Collection turns into collection of one type ◮ No longer requires to be collection of pointers ◮ Possible to have advantages in terms of performance

and type-safety // draw a collection of all kinds of shapes void draw_shapes(std::vector<GeoObj*> const& elems) { for(unsigned int i = 0; i < elems.size(); ++i) elems[i]->draw(); }

slide-40
SLIDE 40

[Faculty of Science Information and Computing Sciences] 40

◮ Now using a template parameter ◮ Collection turns into collection of one type ◮ No longer requires to be collection of pointers ◮ Possible to have advantages in terms of performance

and type-safety // draw a collection of one kind of shape template <typename GeoObj> void draw_shapes(std::vector<GeoObj> const& elems) { for(unsigned int i = 0; i < elems.size(); ++i) elems[i].draw(); }

slide-41
SLIDE 41

[Faculty of Science Information and Computing Sciences] 41

// calculate the distance using the centers // of the shapes Coordinate distance_between(GeoObj const& x1, GeoObj const& x2) { return x1.center_of_gravity()

  • x2.center_of_gravity;

}

slide-42
SLIDE 42

[Faculty of Science Information and Computing Sciences] 42

// calculate the distance using the centers // of the shapes template <typename GeoObj1, typename GeoObj2> Coordinate distance_between(GeoObj1 const& x1, GeoObj2 const& x2) { return x1.center_of_gravity()

  • x2.center_of_gravity;

}

slide-43
SLIDE 43

[Faculty of Science Information and Computing Sciences] 43

Dynamic vs Static

Dynamic strengths

◮ Multityped collections are handled easier ◮ Executable code size is potentially smaller ◮ Code can be entirely compiled

Static strengths

◮ Often regarded more type-safe, binding is checked at

compile-time

◮ Generated code is potentially faster (no indirection

through pointers and no layer of virtual functions)

slide-44
SLIDE 44

[Faculty of Science Information and Computing Sciences] 44

Generic Programming

◮ Finding abstract representation of effjcient algorithms ◮ Already widely used in STL (Standard Template Library) ◮ STL provides a number of useful algorithms to use for

collection objects

◮ Algorithms are written in a generic way so it can be

used with every kind of collection which uses the concept of Iterators –

slide-45
SLIDE 45

[Faculty of Science Information and Computing Sciences] 45

STL: Iterators

template<typename Iterator> Iterator max_element(Iterator first, Iterator last) { if (first == last) { return last; } Iterator largest = first; ++first; for (; first != last; ++first) { if (*largest < *first) { largest = first; } } return largest; }

(from cppreference.com)

slide-46
SLIDE 46

[Faculty of Science Information and Computing Sciences] 46

STL: Vector

template<typename T, ... > class vector { public: // implementation-specific iterator // type for constant vectors typedef ... const_iterator; const_iterator begin() const; const_iterator end() const; };

slide-47
SLIDE 47

[Faculty of Science Information and Computing Sciences] 47

template <typename T> void print_max (T const& coll) { // Declare local iterator of collection typename T::const_iterator pos; // compute position of maximum value pos = std::max_element(coll.begin(), coll.end()); // print value of maximum element std::cout << *pos << std::endl; }

slide-48
SLIDE 48

[Faculty of Science Information and Computing Sciences] 48

STL Iterators

◮ Using these iterators, algorithms like max_element only

have to be created once and can be used by any collection type that makes use of this iterator concept

◮ Could probably be implemented using dynamic

polymorphism, but this is a really lightweight and simple approach: using a layer of virtual functions will probably slow down the operations.

slide-49
SLIDE 49

[Faculty of Science Information and Computing Sciences] 49

Traits

Traits are used to extract properties from generic type

◮ Example:

template< typename T > struct is_integral { static const bool value; typedef std::integral_constant<bool, value> type; }; Return integrality of type via T:type

slide-50
SLIDE 50

[Faculty of Science Information and Computing Sciences] 50

Policies

Policies are used to inject behavior into parent class

◮ Example:

template<typename T, typename Allocator = std::allocator<T>> class vector; Inject memory allocation into vector, default std::allocator<T>

slide-51
SLIDE 51

[Faculty of Science Information and Computing Sciences] 51

Accumulator: Idea

Let’s accumulate some items… Take the array consisting of 5 integers

◮ int num[] = {1,2,3,4,5}

Function accum accumulates items from beg to end accum(&num[0], &num[5]) returns 15 … But should work for any type T!

slide-52
SLIDE 52

[Faculty of Science Information and Computing Sciences] 51

Accumulator: Idea

Let’s accumulate some items… Take the array consisting of 5 integers

◮ int num[] = {1,2,3,4,5}

Function accum accumulates items from beg to end accum(&num[0], &num[5]) returns 15 … But should work for any type T!

slide-53
SLIDE 53

[Faculty of Science Information and Computing Sciences] 51

Accumulator: Idea

Let’s accumulate some items… Take the array consisting of 5 integers

◮ int num[] = {1,2,3,4,5}

Function accum accumulates items from beg to end accum(&num[0], &num[5]) returns 15 … But should work for any type T!

slide-54
SLIDE 54

[Faculty of Science Information and Computing Sciences] 52

Accumulator: First try

template <typename T> T accum (T const* beg, T const* end) { T total = T(); while (beg != end) { total += *beg; ++beg; } return total; } Correct result for int Can result in incorrect for char (negative numbers)

slide-55
SLIDE 55

[Faculty of Science Information and Computing Sciences] 52

Accumulator: First try

template <typename T> T accum (T const* beg, T const* end) { T total = T(); while (beg != end) { total += *beg; ++beg; } return total; } Correct result for int Can result in incorrect for char (negative numbers)

slide-56
SLIDE 56

[Faculty of Science Information and Computing Sciences] 52

Accumulator: First try

template <typename T> T accum (T const* beg, T const* end) { T total = T(); while (beg != end) { total += *beg; ++beg; } return total; } Correct result for int Can result in incorrect for char (negative numbers)

slide-57
SLIDE 57

[Faculty of Science Information and Computing Sciences] 53

Accumulator: Adding traits template

T is not always a good accumulator type! Specify some accumulator type for every type. char values should be accumulated as int Call this accumulator type AccT AccT is a trait/property of the class of type T

slide-58
SLIDE 58

[Faculty of Science Information and Computing Sciences] 53

Accumulator: Adding traits template

T is not always a good accumulator type! Specify some accumulator type for every type.

◮ char values should be accumulated as int

Call this accumulator type AccT AccT is a trait/property of the class of type T

slide-59
SLIDE 59

[Faculty of Science Information and Computing Sciences] 53

Accumulator: Adding traits template

T is not always a good accumulator type! Specify some accumulator type for every type.

◮ char values should be accumulated as int

Call this accumulator type AccT

◮ AccT is a trait/property of the class of type T

slide-60
SLIDE 60

[Faculty of Science Information and Computing Sciences] 54

Accumulator: Adding traits template

template<typename T> class AccumulationTraits; template<> class AccumulationTraits<char> { public: typedef int AccT; }; template<> class AccumulationTraits<short> { public: typedef int AccT; }; ...

slide-61
SLIDE 61

[Faculty of Science Information and Computing Sciences] 55

Accumulator: Using traits template

Update accum to use traits template

◮ Swap out the original accumulator type T for associated

AccT

◮ Also make AccT the new return type of accum

slide-62
SLIDE 62

[Faculty of Science Information and Computing Sciences] 56

Accumulator: Using traits template

template <typename T> typename AccumulationTraits<T>::AccT accum( T const* beg, T const* end) { typedef typename AccumulationTraits<T>::AccT AccT; AccT total = AccT(); while (beg != end) { total += *beg; ++beg; } return total; }

slide-63
SLIDE 63

[Faculty of Science Information and Computing Sciences] 57

Accumulator: Value Traits

AccT total = AccT();

◮ Problematic if AccT() doesn’t default to zero ◮ Also: AccT might not even have a constructor

Update traits template to contain a zero constant ... template<> class AccumulationTraits<char> { public: typedef int AccT; static AccT const zero = 0; }; ...

slide-64
SLIDE 64

[Faculty of Science Information and Computing Sciences] 57

Accumulator: Value Traits

AccT total = AccT();

◮ Problematic if AccT() doesn’t default to zero ◮ Also: AccT might not even have a constructor

Update traits template to contain a zero constant ... template<> class AccumulationTraits<char> { public: typedef int AccT; static AccT const zero = 0; }; ...

slide-65
SLIDE 65

[Faculty of Science Information and Computing Sciences] 58

Accumulator: Using value traits

template <typename T> typename AccumulationTraits<T>::AccT accum( T const* beg, T const* end) { typedef typename AccumulationTraits<T>::AccT AccT; // initialisation is changed AccT total = AccumulationTraits<T>::zero; while (beg != end) { total += *beg; ++beg; } return total; }

slide-66
SLIDE 66

[Faculty of Science Information and Computing Sciences] 59

Zero problem using value traits

Now the initialisation of the accumulation variable is simply AccT total = AccumulationTraits<T>::zero; But works only for static constant data member of type int

  • r enum.

Invalid ... template<> class AccumulationTraits<float> { public: typedef double AccT; // ERROR: not an integral type static double const zero = 0.0; };

slide-67
SLIDE 67

[Faculty of Science Information and Computing Sciences] 59

Zero problem using value traits

Now the initialisation of the accumulation variable is simply AccT total = AccumulationTraits<T>::zero; But works only for static constant data member of type int

  • r enum.

Invalid ... template<> class AccumulationTraits<float> { public: typedef double AccT; // ERROR: not an integral type static double const zero = 0.0; };

slide-68
SLIDE 68

[Faculty of Science Information and Computing Sciences] 59

Zero problem using value traits

Now the initialisation of the accumulation variable is simply AccT total = AccumulationTraits<T>::zero; But works only for static constant data member of type int

  • r enum.

Invalid ... template<> class AccumulationTraits<float> { public: typedef double AccT; // ERROR: not an integral type static double const zero = 0.0; };

slide-69
SLIDE 69

[Faculty of Science Information and Computing Sciences] 60

Zero problem workaround

Update accumulation traits once more ... template<> class AccumulationTraits<float> { public: typedef double AccT; static AccT zero() { return 0; } }; ...

slide-70
SLIDE 70

[Faculty of Science Information and Computing Sciences] 61

Zero problem workaround: function

The accum functions now uses a function to initialise the total

◮ AccT total = AccumulationTraits<T>::zero();

instead of

◮ AccT total = AccumulationTraits<T>::zero;

slide-71
SLIDE 71

[Faculty of Science Information and Computing Sciences] 62

Introducing Policies

Thus far accumulation = summation But also possible

◮ accumulation = multiplication ◮ accumulation = concatenation ◮ …

accum can remain the same, except for total += *start. This is the policy of the accumulation.

slide-72
SLIDE 72

[Faculty of Science Information and Computing Sciences] 62

Introducing Policies

Thus far accumulation = summation But also possible

◮ accumulation = multiplication ◮ accumulation = concatenation ◮ …

accum can remain the same, except for

◮ total += *start.

This is the policy of the accumulation.

slide-73
SLIDE 73

[Faculty of Science Information and Computing Sciences] 63

Accumulator: Add Policy

◮ Create a Policy with an accumulate function ◮ Pass the Policy to accum ◮ Now use the Policy’s accumulator

accum doesn’t care about the implementation of accumulator, as long as it works

slide-74
SLIDE 74

[Faculty of Science Information and Computing Sciences] 64

Accumulator: Add Policy

template <typename T, typename Policy = SumPolicy, typename Traits = AccumulationTraits<T> > class Accum { public: typedef typename Traits::AccT AccT; static AccT accum (T const* beg, T const* end) { AccT total = Traits::zero(); while (beg != end) { Policy::accumulate(total, *beg); ++beg; } return total; } };

slide-75
SLIDE 75

[Faculty of Science Information and Computing Sciences] 65

Difgerent Policies: Sum

Where the Policy can be a sum class SumPolicy { public: template<typename T1, typename T2> static void accumulate ( T1& total, T2 const & value) { total += value; // sum } };

slide-76
SLIDE 76

[Faculty of Science Information and Computing Sciences] 66

Difgerent Policies: Multiplication

… or a multiplication class MultiplicationPolicy { public: template<typename T1, typename T2> static void accumulate ( T1& total, T2 const& value) { total *= value; // multiplication } };

  • r some other accumulation function.
slide-77
SLIDE 77

[Faculty of Science Information and Computing Sciences] 66

Difgerent Policies: Multiplication

… or a multiplication class MultiplicationPolicy { public: template<typename T1, typename T2> static void accumulate ( T1& total, T2 const& value) { total *= value; // multiplication } };

  • r some other accumulation function.
slide-78
SLIDE 78

[Faculty of Science Information and Computing Sciences] 67

Summary Traits and Policies

Use Traits and Policies to

◮ Extract properties from specifjc specialisations of

generic types

◮ Add behaviour to specifjc specialisations of generic

types Advantage:

◮ Generic code unchanged, except for the

interchangeable trait/policy reference

◮ Easy to incorporate new types

slide-79
SLIDE 79

[Faculty of Science Information and Computing Sciences] 68

Determining Element Types

Given some container types vector<T>, list<T>, stack<T> what is the element type T? Example: vector<int> has element type int stack< <stack<bool> > has element type stack<bool>

slide-80
SLIDE 80

[Faculty of Science Information and Computing Sciences] 68

Determining Element Types

Given some container types vector<T>, list<T>, stack<T> what is the element type T? Example:

◮ vector<int> has element type int ◮ stack< <stack<bool> > has element type stack<bool>

slide-81
SLIDE 81

[Faculty of Science Information and Computing Sciences] 69

Element Type via partial specialization

template <typename T> // primary template class ElementT; template <typename T> // partial specialization class ElementT<std::vector<T> > { public: typedef T Type; }; template <typename T> // partial specialization class ElementT<std::list<T> > {} public: typedef T Type; };

slide-82
SLIDE 82

[Faculty of Science Information and Computing Sciences] 70

Get Element Type

typeid(typename ElementT<T>::Type).name() returns the element type of container type T

slide-83
SLIDE 83

[Faculty of Science Information and Computing Sciences] 71

Template metaprogramming (C++)

Template metaprogramming

◮ Generates temporary source code which is merged with

the rest of the source code before compilation

◮ Is a primitive recursive language that can be seen as a

form of functional programming

◮ Used for data structures and non-trivial computations

at compile time

◮ Is a Turing-complete language ◮ Is difgerent from macro’s: mutable variables are not

allowed

slide-84
SLIDE 84

[Faculty of Science Information and Computing Sciences] 72

Metaprogram - A Simple example

An example: How to compute a power of 3? The idea: 3n = 3 · 3n−1 (1) 30 = 1 (2)

slide-85
SLIDE 85

[Faculty of Science Information and Computing Sciences] 73

Metaprogram - A Simple example

//primary template: template<int N> class Pow3 { enum { result=3*Pow3<N-1>::result}; }; //specialization to end recursion template<> class Pow3<0> { enum { result=1}; }; cout << Pow3<7>::result << endl; #> 2187

slide-86
SLIDE 86

[Faculty of Science Information and Computing Sciences] 74

//primary template: template<int N> class Pow3 { enum { result=3*Pow3<N-1>::result}; }; //specialization to end recursion template<> class Pow3<0> { enum { result=1}; }; cout << Pow3<7>::result << endl; #> 2187 Note: using static constant members causes the computation to be no longer limited to a pure “compile time” efgect. Use enumeration values instead!

slide-87
SLIDE 87

[Faculty of Science Information and Computing Sciences] 75

Metaprogram - A Simple example

Pow3<7> requires instantiation of the same template for 6, its result is 3 * Pow3<6>::result . The recursion stops when Pow3<> is instantiated over zero The Pow3<> template (including its specilization) is called a template metaprogram

slide-88
SLIDE 88

[Faculty of Science Information and Computing Sciences] 76

Metaprogram - Another example

Computation of a square root. We need to do this recurively! The idea: use the same procedure as binary search to compute the root: int findRoot(int n, int l, int r) { if (l==r) return l; //compute midpoint, rounded up: int mid = (l + r + 1) / 2; //search in a halved interval: int result = (n<mid*mid) ? findRoot(n, l, mid - 1) : findRoot(n, mid, r); return result; }

slide-89
SLIDE 89

[Faculty of Science Information and Computing Sciences] 77

Metaprogram - Another example

In the template metaprogramming language this becomes: //primary template template <int N, int LO=1, int HI=N> class Sqrt { //compute the midpoint, rounded up: enum { mid = (LO+HI/2) }; //search in a halved interval: enum { result= (N<mid*mid) ? Sqrt<N,LO,mid-1>::result : Sqrt<N,mid,HI>::result }; }; //partial specilization for the case LO == HI: template<int N, int M> class Sqrt<N,M,M> { enum { result=M}; };

slide-90
SLIDE 90

[Faculty of Science Information and Computing Sciences] 78

Metaprogram - Another example

cout << Sqrt<16>::result << endl #> 4 The expression Sqrt<16>::result will expand to Sqrt<16,1,16>::result inside the template. Both Sqrt<N,LO,mid-1> and Sqrt<N,mid,HI> are instantiated, this can easily result in many instantiations!

slide-91
SLIDE 91

[Faculty of Science Information and Computing Sciences] 79

Metaprogram - Another example

◮ Template instantiation is a expensive process for most

compilers

◮ An explosion of template instantiations can be avoided

by using specilizations

◮ Specilizations are used to select the result of

computation instead of using condition operator ? :

◮ As a workaround, one may also use a special template

that takes a Boolean to select a path in recursion tree

slide-92
SLIDE 92

[Faculty of Science Information and Computing Sciences] 80

Metaprogram - Unrolling loops

An example with the dot-product: a[0]*b[0]+a[1]*b[1]+a[2]*b[2] Problems:

◮ Straightforward implementation of a template using a

for-loop is not suffjcient.

◮ The compiler usually optimizes loops, which is

counterproductive in this case! Solution: Use an extra variable for the dimension to recursively unroll the loop

slide-93
SLIDE 93

[Faculty of Science Information and Computing Sciences] 81

Metaprogram - Unrolling loops

Solution: Use an extra variable for the dimension to recursively unroll the loop: //primary template: template <int DIM, typename T> class DotProduct { static T result (T* a, T* b) { return *a * *b + DotProduct<DIM-1, T>::result(a+1, b+1); } }; //partial specilization as end criteria: template <typename T> class DotProduct<1,T> { static T result (T* a, T* b) { return *a * *b; } };

slide-94
SLIDE 94

[Faculty of Science Information and Computing Sciences] 82

Metaprogram - Unrolling loops

We can also introduce a convenience function : template<int DIM, typename T> inline T dot_product (T* a, T* b) { return DotProduct<DIM,T>::result(a,b); } The template can be instantiated by dot_product<S>(a,b) where S is the dimension of the vectors a and b Note: using static member functions are implicitely inline

slide-95
SLIDE 95

[Faculty of Science Information and Computing Sciences] 83

Metaprogram - Unrolling loops

Results: int a[3] = { 1, 2, 3}; int b[3] = { 5, 6, 7}; dot_product<3>(a,b) now unrolls into: = *a * *b + DotProduct<2,int>::result(a+1, b+1) = *a * *b + *(a+1) * *(b+1) + *(a+2) * *(b+2) This is a very effjcient and optimized way to perform huge vector computations!

slide-96
SLIDE 96

[Faculty of Science Information and Computing Sciences] 84

Metaprogram - Data structures: Lists

Template metaprogramming can be used to implement data structures at compile time, for example Lists Think of a list as tuples of tuples, for example: (1, (2, (3, NIL))) template <typename H, typename T=NIL> struct Lst { typedef H Head; typedef T Tail; }; All basic list operations can be implemented with (recursive) template programming Note: This is very similar to Haskell

slide-97
SLIDE 97

[Faculty of Science Information and Computing Sciences] 85

Metaprogram - Data structures: Lists

Getting the lenght of a list: //primary template template <typename LST> struct Lenght { enum{ result = 1 + Lenght< typename LST::Tail >::result }; }; //specialization template <> struct Lenght<NIL> { enum { result = 0}; }; Note: typename needs to be written before LST::Tail to make sure the compiler treats LST::Tail as a type

slide-98
SLIDE 98

[Faculty of Science Information and Computing Sciences] 86

Metaprogram - Data structures: Fractions

Template metaprogramming may also be used for abstract

  • perations on a data structure, for example on fractions:

◮ Addition, substraction, multiplication, division ◮ Simplication rules of expressions

template<int N, int D> struct Frac { static const long Num = N; static const long Den = D; }; template<int N, typename F> struct ScalarMultiplication { typedef Frac<N*F::Num, F::Den> result; }; ... etc.

slide-99
SLIDE 99

[Faculty of Science Information and Computing Sciences] 87

Metaprogram - Summary

A template metaprogram can contain:

◮ State variables: the template parameters ◮ Loop constructs: through recursion ◮ Path selection: by using conditional expressions or

specilizations

◮ Integer arithmetic