[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
[Faculty of ScienceInformation and Computing
Sciences]1
Concepts of programming languagesC++ Template Metaprogramming
Michiel Magendans Robin Ganpat Jelmer van NussHugo Heemskerk Thomas Dirkse
[Faculty of ScienceInformation and Computing
Sciences]2
Metaprogramming
Why programmetaprograms?
I Programs writing programs, we’ve seen this beforeI An introduction, not limited to C++I Very useful in the real world!I Can go horribly wrong..
[Faculty of ScienceInformation and Computing
Sciences]3
Hypothetical company
I Wemake administration software! Exciting!I Web application that allows us to interact with adatabase
I Some business logic when interacting with the database
[Faculty of ScienceInformation and Computing
Sciences]4
What do we want to make?
I CRUD (Create, Read, Update, Delete) software requiresinsert, update, etc. functions/statements
I A database table is essentially a small set of namedparameters, which themselves can have parameters
I Boilerplate heaven! So much code to write!I Functions, getters, setters, loving itI Always write the general case, don’t abstract
[Faculty of ScienceInformation and Computing
Sciences]5
How to monetize this
I Charge customer by programmer hourI Hire lots more programmersI OutsourceI Profit!
[Faculty of ScienceInformation and Computing
Sciences]6
Lots of work though
I Work is getting cumbersome, let’s trymetaprogramming
Figure 1:
[Faculty of ScienceInformation and Computing
Sciences]7
Let’s reduce our workload
I Idea: Write a code generator! It writes the code for us!I Set of 12 database table parameters to create 2k LoC,spread out over 16 files
I Stringly typed, some extra xml, no documentationnecessary
I Manually add every generated file to a complex file tree
[Faculty of ScienceInformation and Computing
Sciences]8
More profit!
I We have now guaranteed ourselves work for years tocome!
I When the generator changes, we will have toretroactively update all previously generated files
I A job that would normally cost 30 minutes will now cost10 hours at the minimum: we will make so much money!
I Technical debt? Owed by our customers, to us? Brilliant!
[Faculty of ScienceInformation and Computing
Sciences]9
Derp
Figure 2:
[Faculty of ScienceInformation and Computing
Sciences]10
But.. why?
I This is not due to incompetence!I It’s a set of common business problems comingtogether in an unfortunate way
I Strategy problems: no short-term financial incentives towork efficiently, historical artifacts
I People problems: Unwillingness to change, attachmentto own code, egos
I People problems are usually caused by strategyproblems
[Faculty of ScienceInformation and Computing
Sciences]11
What should happen in the business
I Different business model can make a huge difference!I Upfront single-price purchaseI SaaS (Software as a Service, subscription fee)I Different languages can prevent different types ofsituations from happening in the first place
[Faculty of ScienceInformation and Computing
Sciences]12
What should happen in the tech
I DRY: Don’t Repeat YourselfI Make the generic case trivialI Extensibility: allow boilerplate to be written iffnecessary, and let it override very specific parts of themeta program
I Never let programmers touch the generated code - itmight as well not exist
I Allow business logic to interact with metaprogrammedclasses as if they were written manually
I IDE/text editor support, humans are not good atspotting syntax errors
[Faculty of ScienceInformation and Computing
Sciences]13
In OOP?
I You can do this in pure OOP too!I However: requires multifaceted investment inframework
I Business logic becomes harder to separate from theframework
I Onboarding/training new developers becomes harderI Language/IDEs/autocompletion might not becooperative
[Faculty of ScienceInformation and Computing
Sciences]14
There are costs and obstacles
I If your chosen language does not support any types ofmetaprogramming, expect heavy costs, or rather don’ttry at all
I Requires a higher upfront investment regardlessI Can be tougher to debugI Small changes in the meta program affect everything
[Faculty of ScienceInformation and Computing
Sciences]15
Takeaway
I Metaprogramming can be extremely useful andextremely damaging if done wrong
I Choice in language and programming practices greatlyaffects how a business is, and can be run
I The opposite is also true!I The question you need to ask yourself when choosing alanguage is: What mistakes will I allow the company’sprogrammers to make? What bugs are worth their time?
[Faculty of ScienceInformation and Computing
Sciences]16
C++ Templates
What are they?
I A feature of C++ which allows functions and classes tooperate with generic types.
I Generics are generic until the types are substituted forthem at runtime. Templates are specialized at compiletime.
I Templates allow explicit specialization.I Type substition is done during compilation, notrun-time.
I There are Function Templates and Class Templates
[Faculty of ScienceInformation and Computing
Sciences]17
Function Templates - A Simple example
A possible Max()-function defined in C++:
int Max(int a, int b){
return a > b ? a : b;}
Result:
cout << Max(23, 40);#> 40
[Faculty of ScienceInformation and Computing
Sciences]18
A Simple example
The same function as previous slide, but for the double datatype.
double Max(double a, double b){
return a > b ? a : b;}
Result:
cout << Max(12.5, 48.21);#> 48.21
[Faculty of ScienceInformation and Computing
Sciences]19
So what’s the difference?
It is exactly the same logic, but the data type isdifference.
This is where Templates come in handy!
[Faculty of ScienceInformation 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. Thefunction returns a value of int type.
[Faculty of ScienceInformation and Computing
Sciences]21
Class templates
I Function templates allow writing generic functions thatwill work on many types.
I The same idea applies for class templates.
[Faculty of ScienceInformation 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 ScienceInformation 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 ScienceInformation and Computing
Sciences]24
Template Specialization
In some cases you may want a specific implementationfor a specific data type.
This is where Template Specialization comes in.
[Faculty of ScienceInformation and Computing
Sciences]25
Template Specialization
ValueComparer Template definition 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 ScienceInformation and Computing
Sciences]26
Lets say we want to create a specific implementation for thedata type: string. The function getBiggerOne() should returnthe 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 ScienceInformation 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 ScienceInformation and Computing
Sciences]28
Generic Programming
I PolymorphismI Widely used concept in OO languagesI Used to associate different specific behaviours with asingle generic notation
[Faculty of ScienceInformation and Computing
Sciences]29
I In C++, you make use of inheritance and virtualfunctions
Figure 3: C++ Polymorphism
[Faculty of ScienceInformation 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 ScienceInformation and Computing
Sciences]31
I After creating the concrete objects, access the differentconcrete implementations using references or pointersto base class.
// Draw any GeoObjvoid draw_shape(GeoObj const& shape){
shape.draw();}
[Faculty of ScienceInformation and Computing
Sciences]32
// draw a collection of all kinds of shapesvoid 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 shapesCoordinate distance_between(GeoObj const& x1,
GeoObj const& x2){
return x1.center_of_gravity()- x2.center_of_gravity;
}
[Faculty of ScienceInformation and Computing
Sciences]33
Dynamic Polymorphism
I Compiler does not know at compile time which versionof the virtual function “draw” is used
I At runtime the right “draw” function is invokeddynamically using the virtual functions
I This is called Dynamic Polymorphism
[Faculty of ScienceInformation and Computing
Sciences]34
Static Polymorphism
I Using templates, static polymorphism can be used.I Relies on common syntax, rather than commonbehaviour in base class.
[Faculty of ScienceInformation and Computing
Sciences]35
Figure 4: Dynamic C++ Polymorphism
[Faculty of ScienceInformation and Computing
Sciences]36
I No longer relies on base-class
// Concrete circle class, NOT derived from any classclass Circle{public:
// Both methods implementations in .cpp file.void draw() const;Coordinate center_of_gravity() const;
}
Figure 5: Static C++ Polymorphism
[Faculty of ScienceInformation and Computing
Sciences]37
I Now using a template parameter
// Draw any GeoObjvoid draw_shape(GeoObj const& shape){
shape.draw();}
[Faculty of ScienceInformation and Computing
Sciences]38
I Now using a template parameter
// Draw any GeoObjtemplate <typename GeoObj>void draw_shape(GeoObj const& shape){
shape.draw();}
[Faculty of ScienceInformation and Computing
Sciences]39
I Now using a template parameterI Collection turns into collection of one typeI No longer requires to be collection of pointersI Possible to have advantages in terms of performanceand type-safety
// draw a collection of all kinds of shapesvoid draw_shapes(std::vector<GeoObj*> const& elems){
for(unsigned int i = 0; i < elems.size(); ++i)elems[i]->draw();
}
[Faculty of ScienceInformation and Computing
Sciences]40
I Now using a template parameterI Collection turns into collection of one typeI No longer requires to be collection of pointersI Possible to have advantages in terms of performanceand type-safety
// draw a collection of one kind of shapetemplate <typename GeoObj>void draw_shapes(std::vector<GeoObj> const& elems){
for(unsigned int i = 0; i < elems.size(); ++i)elems[i].draw();
}
[Faculty of ScienceInformation and Computing
Sciences]41
// calculate the distance using the centers// of the shapesCoordinate distance_between(GeoObj const& x1,
GeoObj const& x2){
return x1.center_of_gravity()- x2.center_of_gravity;
}
[Faculty of ScienceInformation and Computing
Sciences]42
// calculate the distance using the centers// of the shapestemplate <typename GeoObj1, typename GeoObj2>Coordinate distance_between(GeoObj1 const& x1,
GeoObj2 const& x2){
return x1.center_of_gravity()- x2.center_of_gravity;
}
[Faculty of ScienceInformation and Computing
Sciences]43
Dynamic vs Static
Dynamic strengths
I Multityped collections are handled easierI Executable code size is potentially smallerI Code can be entirely compiled
Static strengths
I Often regarded more type-safe, binding is checked atcompile-time
I Generated code is potentially faster (no indirectionthrough pointers and no layer of virtual functions)
[Faculty of ScienceInformation and Computing
Sciences]44
Generic Programming
I Finding abstract representation of efficient algorithmsI Already widely used in STL (Standard Template Library)I STL provides a number of useful algorithms to use forcollection objects
I Algorithms are written in a genericway so it can beused with every kind of collection which uses theconcept of Iterators
–
[Faculty of ScienceInformation and Computing
Sciences]45
STL: Iteratorstemplate<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 ScienceInformation and Computing
Sciences]46
STL: Vector
template<typename T, ... >class vector {public:
// implementation-specific iterator// type for constant vectorstypedef ... const_iterator;const_iterator begin() const;const_iterator end() const;
};
[Faculty of ScienceInformation and Computing
Sciences]47
template <typename T>void print_max (T const& coll){
// Declare local iterator of collectiontypename T::const_iterator pos;
// compute position of maximum valuepos = std::max_element(coll.begin(), coll.end());
// print value of maximum elementstd::cout << *pos << std::endl;
}
[Faculty of ScienceInformation and Computing
Sciences]48
STL Iterators
I Using these iterators, algorithms likemax_element onlyhave to be created once and can be used by anycollection type that makes use of this iterator concept
I Could probably be implemented using dynamicpolymorphism, but this is a really lightweight andsimple approach: using a layer of virtual functions willprobably slow down the operations.
[Faculty of ScienceInformation and Computing
Sciences]49
Traits
Traits are used to extract properties from generic type
I 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 ScienceInformation and Computing
Sciences]50
Policies
Policies are used to inject behavior into parent class
I Example:
template<typename T, typename Allocator =std::allocator<T>> class vector;
Inject memory allocation into vector, defaultstd::allocator<T>
[Faculty of ScienceInformation and Computing
Sciences]51
Accumulator: Idea
Let’s accumulate some items…
Take the array consisting of 5 integers
I 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 ScienceInformation and Computing
Sciences]51
Accumulator: Idea
Let’s accumulate some items…
Take the array consisting of 5 integers
I 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 ScienceInformation and Computing
Sciences]51
Accumulator: Idea
Let’s accumulate some items…
Take the array consisting of 5 integers
I 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 ScienceInformation 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 ScienceInformation 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 ScienceInformation 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 ScienceInformation and Computing
Sciences]53
Accumulator: Adding traits template
T is not always a good accumulator type!
Specify some accumulator type for every type.
I char values should be accumulated as int
Call this accumulator type AccT
I AccT is a trait/property of the class of type T
[Faculty of ScienceInformation and Computing
Sciences]53
Accumulator: Adding traits template
T is not always a good accumulator type!
Specify some accumulator type for every type.
I char values should be accumulated as int
Call this accumulator type AccT
I AccT is a trait/property of the class of type T
[Faculty of ScienceInformation and Computing
Sciences]53
Accumulator: Adding traits template
T is not always a good accumulator type!
Specify some accumulator type for every type.
I char values should be accumulated as int
Call this accumulator type AccT
I AccT is a trait/property of the class of type T
[Faculty of ScienceInformation 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 ScienceInformation and Computing
Sciences]55
Accumulator: Using traits template
Update accum to use traits template
I Swap out the original accumulator type T for associatedAccT
I Also make AccT the new return type of accum
[Faculty of ScienceInformation 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 ScienceInformation and Computing
Sciences]57
Accumulator: Value Traits
AccT total = AccT();
I Problematic if AccT() doesn’t default to zeroI Also: AccTmight 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 ScienceInformation and Computing
Sciences]57
Accumulator: Value Traits
AccT total = AccT();
I Problematic if AccT() doesn’t default to zeroI Also: AccTmight 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 ScienceInformation 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 changedAccT total = AccumulationTraits<T>::zero;while (beg != end) {
total += *beg;++beg;
}return total;
}
[Faculty of ScienceInformation 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 intor enum.
Invalid
...template<>class AccumulationTraits<float> {public:
typedef double AccT;// ERROR: not an integral typestatic double const zero = 0.0;
};
[Faculty of ScienceInformation 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 intor enum.
Invalid
...template<>class AccumulationTraits<float> {public:
typedef double AccT;// ERROR: not an integral typestatic double const zero = 0.0;
};
[Faculty of ScienceInformation 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 intor enum.
Invalid
...template<>class AccumulationTraits<float> {public:
typedef double AccT;// ERROR: not an integral typestatic double const zero = 0.0;
};
[Faculty of ScienceInformation 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 ScienceInformation and Computing
Sciences]61
Zero problem workaround: function
The accum functions now uses a function to initialise thetotal
I AccT total = AccumulationTraits<T>::zero();
instead of
I AccT total = AccumulationTraits<T>::zero;
[Faculty of ScienceInformation and Computing
Sciences]62
Introducing Policies
Thus far accumulation = summation
But also possible
I accumulation =multiplicationI accumulation = concatenationI …
accum can remain the same, except for
I total += *start.
This is the policy of the accumulation.
[Faculty of ScienceInformation and Computing
Sciences]62
Introducing Policies
Thus far accumulation = summation
But also possible
I accumulation =multiplicationI accumulation = concatenationI …
accum can remain the same, except for
I total += *start.
This is the policy of the accumulation.
[Faculty of ScienceInformation and Computing
Sciences]63
Accumulator: Add Policy
I Create a Policywith an accumulate functionI Pass the Policy to accumI Now use the Policy’s accumulator
accum doesn’t care about the implementation ofaccumulator, as long as it works
[Faculty of ScienceInformation 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 ScienceInformation and Computing
Sciences]65
Different 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 ScienceInformation and Computing
Sciences]66
Different Policies: Multiplication
…or a multiplication
class MultiplicationPolicy {public:
template<typename T1, typename T2>static void accumulate (
T1& total, T2 const& value) {total *= value; // multiplication
}};
or some other accumulation function.
[Faculty of ScienceInformation and Computing
Sciences]66
Different Policies: Multiplication
…or a multiplication
class MultiplicationPolicy {public:
template<typename T1, typename T2>static void accumulate (
T1& total, T2 const& value) {total *= value; // multiplication
}};
or some other accumulation function.
[Faculty of ScienceInformation and Computing
Sciences]67
Summary Traits and Policies
Use Traits and Policies to
I Extract properties from specific specialisations ofgeneric types
I Add behaviour to specific specialisations of generictypes
Advantage:
I Generic code unchanged, except for theinterchangeable trait/policy reference
I Easy to incorporate new types
[Faculty of ScienceInformation and Computing
Sciences]68
Determining Element Types
Given some container types
vector<T>, list<T>, stack<T>
what is the element type T?
Example:
I vector<int> has element type intI stack< <stack<bool> > has element type stack<bool>
[Faculty of ScienceInformation and Computing
Sciences]68
Determining Element Types
Given some container types
vector<T>, list<T>, stack<T>
what is the element type T?
Example:
I vector<int> has element type intI stack< <stack<bool> > has element type stack<bool>
[Faculty of ScienceInformation and Computing
Sciences]69
Element Type via partial specialization
template <typename T>// primary templateclass ElementT;
template <typename T>// partial specializationclass ElementT<std::vector<T> > {public:
typedef T Type;};
template <typename T>// partial specializationclass ElementT<std::list<T> > {}public:
typedef T Type;};
[Faculty of ScienceInformation and Computing
Sciences]70
Get Element Type
typeid(typename ElementT<T>::Type).name()
returns the element type of container type T
[Faculty of ScienceInformation and Computing
Sciences]71
Template metaprogramming (C++)
Template metaprogramming
I Generates temporary source code which is merged withthe rest of the source code before compilation
I Is a primitive recursive language that can be seen as aform of functional programming
I Used for data structures and non-trivial computationsat compile time
I Is a Turing-complete languageI Is different frommacro’s: mutable variables are notallowed
[Faculty of ScienceInformation 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 ScienceInformation 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 recursiontemplate<> class Pow3<0> {
enum { result=1};};
cout << Pow3<7>::result << endl;#> 2187
[Faculty of ScienceInformation and Computing
Sciences]74
//primary template:template<int N> class Pow3 {
enum { result=3*Pow3<N-1>::result};};//specialization to end recursiontemplate<> class Pow3<0> {
enum { result=1};};
cout << Pow3<7>::result << endl;#> 2187
Note: using static constant members causes the computationto be no longer limited to a pure “compile time” effect. Useenumeration values instead!
[Faculty of ScienceInformation 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 atemplate metaprogram
[Faculty of ScienceInformation 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 tocompute 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 ScienceInformation and Computing
Sciences]77
Metaprogram - Another example
In the template metaprogramming language this becomes:
//primary templatetemplate <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 ScienceInformation and Computing
Sciences]78
Metaprogram - Another example
cout << Sqrt<16>::result << endl#> 4
The expression Sqrt<16>::resultwill expand toSqrt<16,1,16>::result inside the template.
Both Sqrt<N,LO,mid-1> and Sqrt<N,mid,HI> areinstantiated, this can easily result in many instantiations!
[Faculty of ScienceInformation and Computing
Sciences]79
Metaprogram - Another example
I Template instantiation is a expensive process for mostcompilers
I An explosion of template instantiations can be avoidedby using specilizations
I Specilizations are used to select the result ofcomputation instead of using condition operator ? :
I As a workaround, one may also use a special templatethat takes a Boolean to select a path in recursion tree
[Faculty of ScienceInformation 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:
I Straightforward implementation of a template using afor-loop is not sufficient.
I The compiler usually optimizes loops, which iscounterproductive in this case!
Solution: Use an extra variable for the dimension torecursively unroll the loop
[Faculty of ScienceInformation and Computing
Sciences]81
Metaprogram - Unrolling loopsSolution: Use an extra variable for the dimension torecursively 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 ScienceInformation 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 ScienceInformation 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 efficient and optimized way to perform hugevector computations!
[Faculty of ScienceInformation and Computing
Sciences]84
Metaprogram - Data structures: Lists
Template metaprogramming can be used to implement datastructures 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 ScienceInformation and Computing
Sciences]85
Metaprogram - Data structures: Lists
Getting the lenght of a list:
//primary templatetemplate <typename LST> struct Lenght {
enum{ result = 1 +Lenght< typename LST::Tail >::result };
};//specializationtemplate <> struct Lenght<NIL> {
enum { result = 0};};
Note: typename needs to be written before LST::Tail tomake sure the compiler treats LST::Tail as a type
[Faculty of ScienceInformation and Computing
Sciences]86
Metaprogram - Data structures: Fractions
Template metaprogramming may also be used for abstractoperations on a data structure, for example on fractions:
I Addition, substraction, multiplication, divisionI 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 ScienceInformation and Computing
Sciences]87
Metaprogram - Summary
A template metaprogram can contain:
I State variables: the template parametersI Loop constructs: through recursionI Path selection: by using conditional expressions orspecilizations
I Integer arithmetic