[Faculty of Science Information and Computing Sciences] 1
Concepts of programming languages C++ Template Metaprogramming - - PowerPoint PPT Presentation
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
[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..
[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
[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
[Faculty of Science Information and Computing Sciences] 5
How to monetize this
◮ Charge customer by programmer hour ◮ Hire lots more programmers ◮ Outsource ◮ Profjt!
[Faculty of Science Information and Computing Sciences] 6
Lots of work though
◮ Work is getting cumbersome, let’s try
metaprogramming
Figure 1:
[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
[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!
[Faculty of Science Information and Computing Sciences] 9
Derp
Figure 2:
[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
[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
[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
[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
[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
[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?
[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
[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
[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
[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!
[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.
[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.
[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(); };
[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
[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.
[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(); }
[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; }
[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
[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
[Faculty of Science Information and Computing Sciences] 29
◮ In C++, you make use of inheritance and virtual
functions
Figure 3: C++ Polymorphism
[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; }
[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(); }
[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;
}
[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
[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.
[Faculty of Science Information and Computing Sciences] 35
Figure 4: Dynamic C++ Polymorphism
[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
[Faculty of Science Information and Computing Sciences] 37
◮ Now using a template parameter
// Draw any GeoObj void draw_shape(GeoObj const& shape) { shape.draw(); }
[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(); }
[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(); }
[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(); }
[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;
}
[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;
}
[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)
[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 –
[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)
[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; };
[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; }
[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.
[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
[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>
[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!
[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!
[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!
[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)
[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)
[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)
[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
[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
[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
[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; }; ...
[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
[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; }
[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; }; ...
[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; }; ...
[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; }
[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; };
[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; };
[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; };
[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; } }; ...
[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;
[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.
[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.
[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
[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; } };
[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 } };
[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.
[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.
[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
[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>
[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>
[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; };
[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
[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
[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)
[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
[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!
[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
[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; }
[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}; };
[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!
[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
[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
[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; } };
[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
[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!
[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
[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
[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.
[Faculty of Science Information and Computing Sciences] 87