Está en la página 1de 49

An Introduction to Meta-

programming

Unleashing the power of C++ Templates


Why Meta-Programming?

2
Introduction
Generic programming becoming a dominant
paradigm in C++
The unexplored power of functional programming
languages
Higher levels of abstraction, design and
reusability
A code-generation mechanism (programs that
generate programs!)
Greater efficiency on what can be found at
compile-time

3
Generic Programming
Early 90s when templates were used for
container<T>
STL
How standardization helped
Design patterns
 The ability for templates to model many design patterns
directly
Idea of meta-programming possibility
 Boost
 Loki
 Other libraries
4
Functional programming
Functional programming is there for a long time
 Lambda calculus, Lisp, Scheme, Haskell…
 functions are treated as regular values
Functional programming advantages
 Idioms, patterns and techniques
Polymorphic “higher-order” functions
 Functions that take other functions as arguments and
returns functions!
The ‘lambda’ thing
 Creating functions ‘on-the-fly’ is very common
TMP is inspired by functional programming
5
Abstraction, Design and
Reusability
The much hyped ‘component software’
 ‘Plug-and-play’ software
Design Patterns
 The GoF (Gang of Four) ‘Design Patterns’ book
 Elements of reusable software
 Language independent
Library as the basis of reusable software (as
opposed to frameworks)
The proverbial ‘adding a level of indirection’ as a
means for better reusable components

6
Program Generating Programs

That is what TMP is all about


Template is not the code that executes
 Generates the code that will do the actual work
Abstract the details
 Select the behavior
 Instruct the components you want to generate

Evaluate whatever is possible at compile-


time
7
Efficiency
Templates can be unimaginably powerful and
efficient
Execution is moved on to compile-time
 The compiler does the work for you instead of the
program!
Higher levels of abstraction, design and
reusability
A code-generation mechanism (programs that
generate programs!)
Greater efficiency on what can be found at
compile-time
8
Template (tricky) Basics

9
Parameterized Types

Templates started Stroustrup’s desire to


provide "parameterized types" for
containers.
A simple example would be a stack:

10
“Parameterized” Stack
template <class T>
class stack{
stack(int start_size = 10);
int is_empty() const;
int is_full() const;
// these members remain the same
void push(const T& t);
T& pop();
// templatizing the required members
private:
T* stack; // Note T* here
int top_of_stack;
int size;
};
11
Comparing with Java example
Making use of Object base class
 Not type safe – the single biggest problem Java
programmers face after performance issues
“Generic types” for typesafety – not for genericity
per se
class Stack<Type> {
public void push(Type t){ /* … */ }
public Type pop() { /*... */ }
private int topOfStack;
private int size = 10;
T stack []; { stack = new stack[size]; }
}
Similarity of C++ templates and Java generics end
here! 12
Templates for Reusable
Components
An example: Making classes non-inheritable
C++ has no equivalent of Java's final
 (for class non-inheritable classes)
You can make destructor of base class private
 disallows creating objects (except in heap)
Disadvantage: imposes constraints
 on kind of type of allocation being done.

13
Example: ‘Plain’ Code
class Usable;
struct Usable_lock {
friend class Usable;
private:
Usable_lock(){}
// copy ctr also private
};
struct Usable : public virtual Usable_lock{
Usable(); // copy ctr also public
// ...
};

14
Example: ‘Plain’ Code …
Usable a;

class DD : public Usable { };

DD dd;
// error: DD::DD() cannot access
// Usable_lock::Usable_lock()
// which is a private member

15
Example: ‘Templatized’ Code
template <typename T> struct nonderivable;

template< class T > class nonderivable_base{


friend T;
// warning, this is non-standard!
friend class nonderivable<T>;
nonderivable_base(){}
nonderivable_base(const nonderivable_base&){}
};

template <typename T>


struct nonderivable: public virtual
nonderivable_base<T>{};

16
Example: ‘Templatized’ Code …
class Test: nonderivable <Test> {};

Test t;
// okay, you can create objects

class Deri: Test{};


// no, you can't inherit & use the object

Deri d;
// Error, in-accessible base class constructor.
//See, compiler won't allow you to do so.

17
Template Template Parameters

One of the interesting properties of


templates
 parameter can be an instantiation of the same
template. For example, vector < vector<int> >
Template Template provide next level of
abstraction
 The template (not its instance) itself can
become an argument

18
Template Template Parameters

template <
typename type,
template <typename T, typename A>
class container = std::vector,
// default is vector if you don’t provide
one explicitly
typename allocator = std::allocator<type> >
class my_container {
container<type, allocator> _container;
//instantiate template template parameter
};
// instantiation of my_container
my_container<int, std::list> container;

19
Template Template P arameters …

In many cases, you can live without it.


For example,std <stack> container
namespace std {
template <class T, class Container = deque<T> >
class stack;
}

Another example is allocator used for containers


namespace std {
template <class T, class Allocator = allocator<T> >
class vector;
}

20
Meta-Programming

21
How it started?
Started with a mistake which led to infinite
recursion in instantiating templates
Initial examples seem simple and for fun
 But has appeal for serious design and industrial
use
Earliest documented example is by Erwin
Unruh for computing Prime numbers.
Initially popularized by Todd Veldhuizen
 Used in Blitz++

22
Serendipity
Infinite instantiation:
template <int n>
struct Factorial {
static const int val =
n * Factorial<n-1>::val;
};
static const int val = Factorial<3>::val;
aCC complains:
Error 588:Template generation nesting too deep;
discovered during generation of specialization
'Factorial<(int)-251>'. You may have an
infinite recursive generation.

23
Adding Terminating Condition
template <int n>
struct Factorial {
static const int val = n * Factorial<n-1>::val;
};

template <>
struct Factorial<1> {
static const int val = 1;
};

const int factorial_of_3 = Factorial<3>::val;

24
Programming Constructs

Metaprograms can be thought in terms of


productions (just like recursive programs).
Implementing productions in algorithms
requires control flow constructs.
If, do, while, for, switch… can be expressed
in terms of templates (expression
templates).

25
Template for If-Then
One example: Representing If-Then-Else
condition
 How to select a class member type based on a
condition?
 Say signed or a unsigned one
 One way to achieve this is to have a traits class and
make the selection over there using a boolean non-type
parameter (basis for typetraits library).
 Part of all major meta-programming libraries (Loki,
Boost etc).

26
Template for If-Then
template <typename T, bool isSigned>
struct myMemberSignTrait{
typedef signed T signType;
}; // selected in case isSigned is true

template <typename T, false> // partial specialization


for false
struct myMemberSignTrait{
typedef unsigned T signType;
}; // selected if isSigned is false

27
Template for If-Then
template <typename T, bool isSigned>
class myContainer {
typedef myMemberSignTrait<T, isSigned>
memberSignTrait;
typedef typename containerMemberTrait::signType
signType;
signType member;
// member will be signed or unsigned
// depending on the isSigned bool value
};

28
Improved If-Then
Not elegant to extend (not scalable)
Better to have a separate template class for
doing such a selection.
template <typename T, typename F, bool isSigned>
struct if_select {
typedef T type;
}; // selected in case isSigned is true

// partial specialization for false


template <typename T, typename F>
struct if_select <typename T, typename F, false> {
typedef F type;
}; // selected if isSigned is false

29
Improved If-Then
template <typename T, bool isSigned>
class myContainer {
typedef typename if_select<signed T, unsigned T,
isSigned>::type signType;
signType member;
// ...
};

30
Still better If-Then
template <bool isSigned>
struct if_cond {
template<typename T, typename F>
struct then_select {
typedef T type;
};
}; // selected in case isSigned is true

template <bool isSigned> // partial specialization for false


struct if_select <false> {
template<typename T, typename F>
struct then_select {
typedef F type;
};
}; // selected in case isSigned is true

31
Still better If-Then
template <typename T, bool isSigned>
class myContainer {
typedef typename if_cond<isSigned>::template
then_select<signed T, unsigned T>::type signType;
signType member;
// ...
};

32
Usage Example – if_select
class CompactingGC {
void GC() { … }
};
class MarkAndSweepGC {
void GC() { … }
};

If_cond <GC == COPY, CopyingGC, MarkAndSweepGC>::value


gc;
gc.GC();

33
Another Example- isPointer
template <typename T>
class type_traits{
template <class U> struct pointer_traits{
static const int result = false;
}
// specialization for pointer type
template <class U *> struct pointer_traits{
static const int result = true;
}
public:
static const int is_pointer = pointer_traits<T>::
result;
};
// const bool is_ptr =
type_traits<someTypedef>::is_pointer;

34
Application
of meta-programming

35
Conversion Check Tool
template <class T, class U>
class Conversion{
typedef char Small;
class Big { char dummy[2]; }
static Small Test(U);
static Big Test(...);
static T MakeT();
public:
enum { exists = sizeof(Test(MakeT())) ==
sizeof(Small) };
};

36
Unrolling vector addition
inline void addVect(int size, double* a, double* b,
double* c) {
while (size--)
*c++ = *a++ + *b++;
}

37
Template Unrolling
template<int size>
inline void addVect(double* a, double* b,
double* c) {
*c = *a + *b;
addVect<size-1>(a+1, b+1, c+1);
}
template<>
inline void addVect(double* a, double* b,
double* c) { }

addVect<3>(a, b, c);

38
Effect of unrolling
addVect<3>(a, b, c);
*c = *a + *b;
addVect<2>(a+1, b+1, c+1);
*(c+1) = *(a+1) + *(b+1);
addVect<1>(a+2, b+2, c+2);
*(c+2) = *(a+2) + *(b+2);
addVect<0>(a+3, b+3, c+3);

39
Dot Product
template <int DIM, typename T>
struct DotProduct {
static T result (T* a, T* b){
return *a * *b +
DotProduct<DIM-1,T>::result(a+1,b+1);
}
};

template <typename T>


struct DotProduct<1,T> {
static T result (T* a, T* b) {
return *a * *b;
}
};

40
Dot Product
template <int DIM, typename T>
inline T dot_product (T* a, T* b) {
return DotProduct<DIM,T>::result(a,b);
}

// usage:
int main() {
int a[3] = { 1, 2, 3};
int b[3] = { 5, 6, 7};
std::cout << "dot_product(3,a,b) = "
<< dot_product(3,a,b) << '\n';
std::cout << "dot_product(3,a,a) = " <<
dot_product(3,a,a);
}

41
For Loop Unrolling
template<int n, class B>
struct FOR {
static void loop(int m) {
for (int i = 0; i < m/n; i++) {
UNROLL<n, B>::iteration(i * n);
}
for (int i = 0; i < m%n; i++) {
B::body(n * m/n + i);
}
};

42
Loop Unrolling …
template <int n, class B>
struct UNROLL {
static void iteration(int i) {
B::body(i);
UNROLL<n-1, B>::iteration(i + 1);
}
};
template <class B>
struct UNROLL<0, B> {
static void iteration(int i) { }
};

43
Meta-programming
Libraries

44
Loki
By Andrei Alexaderescu
“Modern C++ Design” book Covers in Detail
Important components:
 Small object allocator
 Typetraits and Typelists
 Implementing Singletons
 Smart Pointers
 Object Factories
 Visitor

45
Boost
Major and wildly growing library
Compatible with STL
Important components:
 Regex
 Threads
 Typetraits
 Lambda
 Binder
 Function Object Wrapper
 FC++ (Functional C++)
 …
46
Future of
Meta-programming

47
Future Looks Rosy
Templates as a means for reusable and adaptive
software
Generic programming
 poised to become dominant paradigm in C++
Metaprogramming libraries
 Started - to be used widely
 10 Boost libraries – candidates for becoming part
standard
 More and more programmers are attracted towards
Functional programming in C++
 From structured => object-oriented => generic =>
functional ! Don’t faint ;-)
48
But it won’t be easy

Using templates have their own problems


 Generic programming is inherently difficult in
nature
 Using templates needs expertise in C++

 Cryptic error messages

 Readability gets affected, negatively


 Metaprograms doesn’t look like C++ code!
 Too soon to tell how industry will receive it

49

También podría gustarte