Introduction to C++ Templates Speaker: Bill Chapman, C++ developer, financial company in...

42
Introduction to C++ Templates Speaker: Bill Chapman, C++ developer, financial company in Mid-Manhattan. Can be reached via: ’[email protected] ' This talk will be based entirely on C++ as of 2003 – no C++11 will be used. This will be an introduction to templates. It is assumed the audience Knows C (including pointer arithmetic). Understands C++ classes, methods, constructors, destructors, references, and 'const'. Is familiar with C++ I/O & the C++ 'std::string' class.

Transcript of Introduction to C++ Templates Speaker: Bill Chapman, C++ developer, financial company in...

Page 1: Introduction to C++ Templates Speaker: Bill Chapman, C++ developer, financial company in Mid-Manhattan. Can be reached via: cplusplus@ccjj.info ' This.

Introduction to C++ Templates

Speaker: Bill Chapman, C++ developer, financial company in Mid-Manhattan.

Can be reached via: ’[email protected]'

This talk will be based entirely on C++ as of 2003 – no C++11 will be used.

This will be an introduction to templates. It is assumed the audience

Knows C (including pointer arithmetic). Understands C++ classes, methods, constructors,

destructors, references, and 'const'. Is familiar with C++ I/O & the C++ 'std::string' class.

Page 2: Introduction to C++ Templates Speaker: Bill Chapman, C++ developer, financial company in Mid-Manhattan. Can be reached via: cplusplus@ccjj.info ' This.

About Templates

Hard to program. Horrible, very long error messages on mistakes. Most C++ programmers rarely write template code,

mostly it's pre-done for them in libraries, which are pretty easy to use.

The STL is the most famous C++ template library, and is pretty much always included nowadays if you install C++ on your machine.

Page 3: Introduction to C++ Templates Speaker: Bill Chapman, C++ developer, financial company in Mid-Manhattan. Can be reached via: cplusplus@ccjj.info ' This.

Outline

1) 'max' function template.

2) 'mySort' function template.

3) Container class template 'MyArray'.

4) Specialized templates to identify categories of types.

5) Using type category identification to enhance 'MyArray'.

6) Introduction to the STL.

7) Generating primes at compile-time via templates.

8) Generating primes at compile-time without even running the executable (making the primes show up in compiler error messages).

Page 4: Introduction to C++ Templates Speaker: Bill Chapman, C++ developer, financial company in Mid-Manhattan. Can be reached via: cplusplus@ccjj.info ' This.

Approaches to 'max' function

We want to have a function to take the maximum of two numbers. One way to do it is with a macro

#define MAX(LHS, RHS) ((LHS) > (RHS) ? (LHS) : (RHS))

This has some disadvantages Not scoped. If anyone, anywhere, declares an identifier

named MAX after that, global or local, they'll get really confusing compile errors.

'Winner' evaluated twice: 'MAX(++i, ++j)', or 'MAX(slowFunction(x), slowFunction(y))'

Page 5: Introduction to C++ Templates Speaker: Bill Chapman, C++ developer, financial company in Mid-Manhattan. Can be reached via: cplusplus@ccjj.info ' This.

'double' function

Another approach is to declare an inline typed function:inlinedouble max(double x, double y) { return x > y ? x : y;}

Inefficient if you were just dealing with 'int's.What if you want to take the 'max' of a type that stored unlimited precision ints – can't reliably be converted to and from a double.

Page 6: Introduction to C++ Templates Speaker: Bill Chapman, C++ developer, financial company in Mid-Manhattan. Can be reached via: cplusplus@ccjj.info ' This.

Solution: Function Template

template <typename T> inlineT max(const T& lhs, const T& rhs) { return lhs > rhs ? lhs : rhs;}

The compiler will match 'T' to any type that is passed to the args of this routine, and create a routine for that type.Drawback – what if T is a huge class – say, big tables for which 'operator>' is defined? We're copying the return value, which is expensive. So we do:

template <typename T> inlineconst T& max(const T& lhs, const T& rhs) { return lhs > rhs ? lhs : rhs;}

Page 7: Introduction to C++ Templates Speaker: Bill Chapman, C++ developer, financial company in Mid-Manhattan. Can be reached via: cplusplus@ccjj.info ' This.

Problems With 'max' Template

'max(4, 7.3)' won't work – passing an int and a float, different types. Template doesn't know which to use, refuses to compile. Templates won't convert types AT ALL when computing types. So suppose we

template <typename A, typename B> inlineconst A& max(const A& a, const B& b) { return a > b ? a : b;}

NOT a good idea – arbitrarily chose 'A', not 'B' as the return type, so 'max(4, 7.3)' will return 7! So we only use the previous implementation.

Page 8: Introduction to C++ Templates Speaker: Bill Chapman, C++ developer, financial company in Mid-Manhattan. Can be reached via: cplusplus@ccjj.info ' This.

Making one-type 'max' work

So we go back to our previous implementation, which is basically what's available in the STL in '#include <algorithm>'.

template <typename T> inlineconst T& max(const T& lhs, const T& rhs) { return lhs > rhs ? lhs : rhs;}

To make 'max(4, 7.3)' work, you have to be explicit and explain to the compiler exactly what return type you want:

max((double) 4, 7.3) // '4' cast to double, → 7.3

max<double>(4, 7.3) // '4' cast to double, → 7.3

Page 9: Introduction to C++ Templates Speaker: Bill Chapman, C++ developer, financial company in Mid-Manhattan. Can be reached via: cplusplus@ccjj.info ' This.

What If Function Template Not Inline?

template <typename T>const T& max(const T& lhs, const T& rhs) { return lhs > rhs ? lhs : rhs;}

When the function is called, the compiler will generate code for a version of the function with the appropriate 'T'. If two different .cpp files call max<int>, there will be multiple copies of the code for the function generated, all but one of which will be discarded by the linker.

There are ways to tell the compiler not to generate code for a function in the current module, basically promising that another module will do it. We won't get into that. The way people usually do function templates, inline or not, is with the code implementing it visible in the .h file, and let the linker sort out redundancies.

Page 10: Introduction to C++ Templates Speaker: Bill Chapman, C++ developer, financial company in Mid-Manhattan. Can be reached via: cplusplus@ccjj.info ' This.

Unix 'qsort' C function

void qsort(void *base, size_t nmemb, size_t size, int (*compar)(const void *, const void *));Can't handle types with complex assignment, copy c'tor, or d'tor -- must be bitwise copyable. No 'std::string's, for example (std::string MIGHT work, sort of by accident, depending on the details of the implementation of 'qsort' and 'std::string').

Have to cast pointers to 'void *'s, throwing away type checking

Have to provide a special 'compar' function, which must take two 'void *' ptrs, and it cannot be inline.

Page 11: Introduction to C++ Templates Speaker: Bill Chapman, C++ developer, financial company in Mid-Manhattan. Can be reached via: cplusplus@ccjj.info ' This.

Bubble Sort Function Template

This function will sort an array, given ptrs to beginning and end.

template <typename TYPE> void mySort(TYPE *begin, TYPE * const end) { for (; begin != end; ++begin) { for (TYPE *rover = begin + 1; rover != end; ++rover) { if (*rover < *begin) { // 'operator<' TYPE tmp(*rover); // copy c'tor *rover = *begin; // assignment *begin = tmp; // assignment } // destructor called on 'tmp' here} } }

Note uses 'operator<', copy c'tor, assignment, and d'tor. All can be inline!!! This could be trivially upgraded to a better sorting algorithm.

Type checking on args, good chance you wanted to implement 'operator<' for TYPE anyway.

STL has 'std::sort' in '#include <algorithm>', along these lines, but much better.

Page 12: Introduction to C++ Templates Speaker: Bill Chapman, C++ developer, financial company in Mid-Manhattan. Can be reached via: cplusplus@ccjj.info ' This.

Example: Container Class

Arrays of complex types are hard to do by hand in C++.

Want to be able to efficiently grow & shrink array.

'new[]' must call default c'tor when you allocate array – might prefer to defer c'tor calling until you call copy c'tor when adding elements.

If forget to destroy something, could leak memory.

Hard to grow & shrink an array – lots of copying, have to do just right, call appropriate c'tors & d'tors.

We’ll create a 'container class' template that will do most of this work for you, for any well-behaved type, including complex types. We'll call it 'MyArray'.

Showing the code to implement 'MyArray' will take several slides.

Page 13: Introduction to C++ Templates Speaker: Bill Chapman, C++ developer, financial company in Mid-Manhattan. Can be reached via: cplusplus@ccjj.info ' This.

Properties of 'MyArray'

Has two properties: 'capacity' and 'size'. 'size' is how many elements there are in the

container. 'capacity' is how many elements there is room for.

Never calls default c'tor. Only copies copy c'tor when it actually has an element to store into the container.

When container is destroyed, destroys all elements contained.

'capacity' can only grow, never shrinks.

Page 14: Introduction to C++ Templates Speaker: Bill Chapman, C++ developer, financial company in Mid-Manhattan. Can be reached via: cplusplus@ccjj.info ' This.

MyArray: Class Declarationtemplate <typename TYPE>class MyArray { // DATA unsigned d_capacity; unsigned d_size; TYPE *d_array; // PRIVATE MANIPULATORS void grow();

public: // CREATORS MyArray(); ~MyArray();

// MANIPULATORS TYPE& operator[](unsigned i); void push_back(const TYPE& t); void pop_back();

// ACCESSORS const TYPE& operator[](unsigned i) const; unsigned size() const;};

Page 15: Introduction to C++ Templates Speaker: Bill Chapman, C++ developer, financial company in Mid-Manhattan. Can be reached via: cplusplus@ccjj.info ' This.

MyArray Imp: C'tor, D'tor

// CREATORStemplate <typename TYPE>MyArray<TYPE>::MyArray() : d_capacity(4) , d_size(0) { // use malloc, not 'new' -- don't want c'tors run on // individual elements

d_array = (TYPE *) malloc(d_capacity * sizeof(TYPE));}

template <typename TYPE>MyArray<TYPE>::~MyArray() { TYPE * const end = d_array + d_size; for (TYPE *pt = d_array; pt < end; ++pt) { pt->~TYPE(); } free(d_array);}

Page 16: Introduction to C++ Templates Speaker: Bill Chapman, C++ developer, financial company in Mid-Manhattan. Can be reached via: cplusplus@ccjj.info ' This.

MyArray Imp: Manipulators

// MANIPULATORStemplate <typename TYPE>TYPE& MyArray<TYPE>::operator[](unsigned i) { return d_array[i];}

template <typename TYPE>void MyArray<TYPE>::push_back(const TYPE& val) { if (d_size == d_capacity) { grow(); } new (d_array + d_size) TYPE(val); // placement new – calls copy // c'tor, doesn’t allocate ++d_size;}

template <typename TYPE>void MyArray<TYPE>::pop_back() { --d_size; (d_array + d_size)->~TYPE(); // explicit d'tor call}

Page 17: Introduction to C++ Templates Speaker: Bill Chapman, C++ developer, financial company in Mid-Manhattan. Can be reached via: cplusplus@ccjj.info ' This.

MyArray Imp: Accessors

// ACCESSORStemplate <typename TYPE>const TYPE& MyArray<TYPE>::operator[](unsigned i) const { return d_array[i];}

template <typename TYPE>unsigned MyArray<TYPE>::size() const { return d_size;}

Page 18: Introduction to C++ Templates Speaker: Bill Chapman, C++ developer, financial company in Mid-Manhattan. Can be reached via: cplusplus@ccjj.info ' This.

MyArray Imp: 'grow'

// PRIVATE MANIPULATORStemplate <typename TYPE>void MyArray<TYPE>::grow() { unsigned newCapacity = d_capacity * 2; TYPE *newArray = (TYPE *) malloc(sizeof(TYPE) * newCapacity); TYPE * const end = d_array + d_size; for (TYPE *pTo = newArray, *pFrom = d_array; pFrom < end; ++pTo, ++pFrom) { new (pTo) TYPE(*pFrom); // calling copy c'tor in-place pFrom->~TYPE(); // explicit call to d'tor } free(d_array); d_array = newArray; d_capacity = newCapacity;}

Page 19: Introduction to C++ Templates Speaker: Bill Chapman, C++ developer, financial company in Mid-Manhattan. Can be reached via: cplusplus@ccjj.info ' This.

Using MyArray: myArray.cpp#include "mySort.h"#include "myArray.h"int main() { MyArray<std::string> mv; const char *data[] = { "woof", "arf", "meow", "grrrr", "chomp", "bite", "maul", "paw", "tear", "mangle", ”run”, ”sniff” }; enum { NUM_DATA = sizeof(data) / sizeof(data[0]) };

for (int i = 0; i < NUM_DATA; ++i) { mv.push_back(data[i]); // const char * gets converted to std::string } assert(mv.size() == NUM_DATA);

mySort(&mv[0], &mv[0] + mv.size());

// verify mv is sorted for (int i = 0; i < mv.size() - 1; ++i) { assert(mv[i] <= mv[i + 1]); }

// print it out std::cout << ”// Produces Output:\n”; for (unsigned i = 0; i < mv.size(); ++i) { std::cout << "// " << mv[i] << std::endl; }

// d'tor called on all the strings in 'mv', and the array in 'mv' is // freed when 'mv' goes out of scope.}

Page 20: Introduction to C++ Templates Speaker: Bill Chapman, C++ developer, financial company in Mid-Manhattan. Can be reached via: cplusplus@ccjj.info ' This.

MyArray.cpp Ouput

// Produces Output:// arf// bite// chomp// grrrr// mangle// maul// meow// paw// run// sniff// tear// woof

Page 21: Introduction to C++ Templates Speaker: Bill Chapman, C++ developer, financial company in Mid-Manhattan. Can be reached via: cplusplus@ccjj.info ' This.

Type Templates vs Function Templates

When a function template is called, the compiler can often infer the 'TYPE' from the types of the arguments passed.

int x = std::max(3, 7);

When declaring a variable of a templatized type, you must explitly specify the type(s) of the template arg(s).

MyArray<int> mv;

Page 22: Introduction to C++ Templates Speaker: Bill Chapman, C++ developer, financial company in Mid-Manhattan. Can be reached via: cplusplus@ccjj.info ' This.

Template Specialization: IsPointer

You can define the same template multiple times, with different types as args. The compiler will choose the most specialized one to call.

template <typename TYPE> // this template will match any 'TYPE'struct IsPointer { enum { VAL = 0 };};

template <typename TYPE> // more specialized: only matches pointersstruct IsPointer<TYPE *> { enum { VAL = 1 };};

. . .

assert(0 == IsPointer<int>::VAL);assert(1 == IsPointer<char *>::VAL);

Page 23: Introduction to C++ Templates Speaker: Bill Chapman, C++ developer, financial company in Mid-Manhattan. Can be reached via: cplusplus@ccjj.info ' This.

Template Specialization: IsFundamental

template <typename TYPE>struct IsFundamental { enum { VAL = 0 };};

template <>struct IsFundamental<char> enum { VAL = 1 };};template <>struct IsFundamental<short> enum { VAL = 1 };};template <>struct IsFundamental<int> enum { VAL = 1 };};// Repeat for long, long long, unsigned char, signed char, unsigned short, unsigned, unsigned long,// unsigned long long, float.template <>struct IsFundamental<double> enum { VAL = 1 };};

. . .

assert(1 == IsFundamental<int >::VAL);assert(1 == IsFundamental<char >::VAL);assert(0 == IsFundamental<FILE >::VAL);assert(0 == IsFundamental<std::ostream>::VAL);assert(0 == IsFundamental<char * >::VAL);

Page 24: Introduction to C++ Templates Speaker: Bill Chapman, C++ developer, financial company in Mid-Manhattan. Can be reached via: cplusplus@ccjj.info ' This.

What About 'const'?

#include "isPointer.h"#include "isFundamental.h"

struct S { int d_i;};

int main() { assert(0 == IsPointer<char >::VAL); assert(1 == IsPointer<char* >::VAL); assert(1 == IsPointer<const char *>::VAL); // const OK assert(0 == IsPointer<S >::VAL); assert(1 == IsPointer<S * >::VAL); assert(1 == IsPointer<const S * >::VAL); // const OK

assert(0 == IsFundamental<std::ostream>::VAL); assert(0 == IsFundamental<S >::VAL); assert(0 == IsFundamental<char * >::VAL); assert(1 == IsFundamental<int >::VAL); assert(1 == IsFundamental<const int >::VAL); // FAILS!!! assert(1 == IsFundamental<const double>::VAL); // FAILS!!!}

Page 25: Introduction to C++ Templates Speaker: Bill Chapman, C++ developer, financial company in Mid-Manhattan. Can be reached via: cplusplus@ccjj.info ' This.

Fix 'IsFundamental' for 'const'

template <typename TYPE>struct IsFundamentalImp { enum { VAL = 0 };};

template <>struct IsFundamentalImp<char> enum { VAL = 1 };};template <>struct IsFundamentalImp<short> enum { VAL = 1 };};// Repeat for int, long, long long, unsigned char, signed char, unsigned short, unsigned,// unsigned long, unsigned long long, float.template <>struct IsFundamentalImp<double> enum { VAL = 1 };};

template <typename TYPE>struct IsFundamental { enum { VAL = IsFundamentalImp<TYPE>::VAL };};template <typename TYPE>struct IsFundamental<const TYPE> { enum { VAL = IsFundamentalImp<TYPE>::VAL };};

Page 26: Introduction to C++ Templates Speaker: Bill Chapman, C++ developer, financial company in Mid-Manhattan. Can be reached via: cplusplus@ccjj.info ' This.

'const' Now Works

#include "isPointer.h"#include "isFundamental.h"

struct S { int d_i;};

int main() { assert(0 == IsPointer<char >::VAL); assert(1 == IsPointer<char* >::VAL); assert(1 == IsPointer<const char *>::VAL); // const OK assert(0 == IsPointer<S >::VAL); assert(1 == IsPointer<S * >::VAL); assert(1 == IsPointer<const S * >::VAL); // const OK

assert(0 == IsFundamental<std::ostream>::VAL); assert(0 == IsFundamental<S >::VAL); assert(0 == IsFundamental<char * >::VAL); assert(1 == IsFundamental<int >::VAL); assert(1 == IsFundamental<const int >::VAL); // const OK!!! assert(1 == IsFundamental<const double>::VAL); // const OK!!!}

Page 27: Introduction to C++ Templates Speaker: Bill Chapman, C++ developer, financial company in Mid-Manhattan. Can be reached via: cplusplus@ccjj.info ' This.

Optimizing 'MyArray' With Specializations

In the implementation of 'MyArray', we can check if the parametrized type is a pointer or fundamental type. If it is, we knowWe never have to call destructors on elements.A bitwise copy will work just as well as the copy constructor.

Using this information, we can produce a more optimized implementation for containers of simple types.

Page 28: Introduction to C++ Templates Speaker: Bill Chapman, C++ developer, financial company in Mid-Manhattan. Can be reached via: cplusplus@ccjj.info ' This.

Optimizing 'MyArray' With Specializations

The methods where we stand to gain from this optimization are 'grow' and the destructor. First, ’grow’. New code in red:

template <typename TYPE>void MyArray<TYPE>::grow() { unsigned newCapacity = d_capacity * 2; TYPE *newArray = (TYPE *) malloc(sizeof(TYPE) * newCapacity); if (0 == IsPointer<TYPE>::VAL && 0 == IsFundamental<TYPE>::VAL) { TYPE * const end = d_array + d_size; for (TYPE *pTo = newArray, *pFrom = d_array; pFrom < end; ++pTo, ++pFrom) { new (pTo) TYPE(*pFrom); pFrom->~TYPE(); } } else { memcpy(newArray, d_array, d_size * sizeof(TYPE)); } free(d_array); d_array = newArray; d_capacity = newCapacity;}

Page 29: Introduction to C++ Templates Speaker: Bill Chapman, C++ developer, financial company in Mid-Manhattan. Can be reached via: cplusplus@ccjj.info ' This.

Optimizing 'MyArray' With Specializations

Now, the destructor. New code in red:

template <typename TYPE>MyArray<TYPE>::~MyArray() { if (0 == IsPointer<TYPE>::VAL && 0 == IsFundamental<TYPE>::VAL) { TYPE * const end = d_array + d_size; for (TYPE *pt = d_array; pt < end; ++pt) { pt->~TYPE(); } } free(d_array);}

Page 30: Introduction to C++ Templates Speaker: Bill Chapman, C++ developer, financial company in Mid-Manhattan. Can be reached via: cplusplus@ccjj.info ' This.

The STL The C++ Standard Template Library

Has functions in '#include <algorithm>- std::max- std::min- std::sort // and many, many others

Has many containers- std::vector // like 'MyArray', but much more powerful- std::set // binary tree, keys only- std::map // binary tree, key + data- std::list // doubly-linked list- std::deque // double-ended queue (pronounced 'deck')- std::hash_set // hash table, keys only, to be replaced by std::unordered_set- std::hash_map // hash table key + data, to be replaced by std::unordered_map // and more

Doc: ”http://www.sgi.com/tech/stl/” “http://www.cplusplus.com/reference/”

Page 31: Introduction to C++ Templates Speaker: Bill Chapman, C++ developer, financial company in Mid-Manhattan. Can be reached via: cplusplus@ccjj.info ' This.

Doing Our Sort With the STL#include <vector>

int main() { const char *data[] = { "woof", "arf", "meow", "grrrr", "chomp", "bite", "maul", "paw", "tear", "mangle", "sniff", "run" }; enum { NUM_DATA = sizeof data / sizeof *data };

std::vector<std::string> mv(data + 0, data + NUM_DATA); assert(mv.size() == NUM_DATA);

std::sort(&mv[0], &mv[0] + mv.size());

// verify mv is sorted for (unsigned u = 0; u < mv.size() - 1; ++u) { assert(mv[u] <= mv[u + 1]); }

std::cout << "// Produces output:\n"; for (unsigned u = 0; u < mv.size(); ++u) { std::cout << "// " << mv[u] << std::endl; }}

Note the constructor for 'mv’.

Page 32: Introduction to C++ Templates Speaker: Bill Chapman, C++ developer, financial company in Mid-Manhattan. Can be reached via: cplusplus@ccjj.info ' This.

Programming With STL Containers

Programmers learn to use the STL Containers as building blocks.

All containers are well-behaved, general-purpose types, with =, ==, <, >, <=, >=, != all defined. Easy to iterate through the elements in any STL container with a loop.

Generally much simpler to build a datastructure out of STL Containers than to build it with naked pointers. And you rarely have to worry about destructing anything or leaking memory. The containers do it all for you.

It is quite common to have a set of vectors, or a map of maps – all combinations are possible.

Page 33: Introduction to C++ Templates Speaker: Bill Chapman, C++ developer, financial company in Mid-Manhattan. Can be reached via: cplusplus@ccjj.info ' This.

Calculating Primes At Compile Time

Everything we've covered so far is potentially extremely useful. Calculating primes at compile time is not – it's MUCH easier to calculate them at run-time.

Someone figured out, and mathematically proved, that you can simulate a turning machine with templates at compile time. So in theory you can do anything. In practice, it's an incredibly inefficient (but quite fun) way to do things. Just don't quit your day job.

Page 34: Introduction to C++ Templates Speaker: Bill Chapman, C++ developer, financial company in Mid-Manhattan. Can be reached via: cplusplus@ccjj.info ' This.

simplePrime.h

Note that templates can take 'int' and 'bool' for template parameters as well as types. They can't take floats or doubles.

template <int NUM, int DIV>struct IsPrime_Aux { enum { VAL = (NUM % DIV) && IsPrime_Aux<NUM, DIV + 1>::VAL };};template <int NUM>struct IsPrime_Aux<NUM, NUM> { // terminates recursion enum { VAL = 1 };};

template <int NUM>struct IsPrime { enum { BOTTOM = NUM < 2 ? NUM : 2, // BOTTOM = min(NUM, 2); VAL = NUM >= 2 && IsPrime_Aux<NUM, BOTTOM>::VAL };};

The lowest prime number is 2. 0 and 1 are not prime.

'IsPrime<NUM>::VAL' will be 1 if 'NUM' is prime and 0 otherwise. This is the simplest way I could think of to do it.

Page 35: Introduction to C++ Templates Speaker: Bill Chapman, C++ developer, financial company in Mid-Manhattan. Can be reached via: cplusplus@ccjj.info ' This.

Calling 'IsPrime'

How do we loop to call 'IsPrime' for a range of numbers? How about:for (int i = 0; i < 100; ++i) { if (IsPrime<i>::VAL) std::cout << i << std::endl;}

Anybody see why there's a problem with this?

'i' is a variable – only types or compile-time constants can be passed in the '<>'s to a template.

We could just line up 100 calls by hand, but we're programmers – such an approach is beneath us. So how do we iterate from 0 to 99 at compile-time?

Page 36: Introduction to C++ Templates Speaker: Bill Chapman, C++ developer, financial company in Mid-Manhattan. Can be reached via: cplusplus@ccjj.info ' This.

simplePrimes.cpp#include "simplePrime.h"

template <int FROM, int TO>struct ShowPrimes { static void run() { ShowPrimes<FROM, TO – 1>::run(); if (IsPrime<TO>::VAL) std::cout << TO << std::endl;} };template <int END>struct ShowPrimes<END, END> { static void run() { if (IsPrime<END>::VAL) std::cout << END << std::endl;} };

int main() { enum { START = 0 }; // max value of START is 923 ShowPrimes<START, START + 100>::run();}

$ ./a.out235711131719,,,

Page 37: Introduction to C++ Templates Speaker: Bill Chapman, C++ developer, financial company in Mid-Manhattan. Can be reached via: cplusplus@ccjj.info ' This.

betterPrime.htemplate <int NUM>

struct IsPrime { template <int DIV, bool DONE = (DIV * DIV > NUM)> struct Aux; // unspecialized declaration, no definition

template <int DIV> struct Aux<DIV, false> { enum { VAL = (NUM % DIV) && Aux<DIV + 2>::VAL }; }; template <int DIV> struct Aux<DIV, true> { enum { VAL = 1 }; };

enum { VAL = 2 == NUM || (NUM > 1 && NUM % 2 && Aux<3>::VAL) };};

This implementation of 'IsPrime<NUM>' is much more efficient than the last one. It special cases the check for even numbers, and other than that only tries to divide odd numbers, up past the square root of 'NUM', at which point it stops, where 'simplePrime.h' tried dividing all numbers, odd and even, from 2 up to 'NUM – 1'.

The means of stopping recursion is interesting. We use a specialized template with the 2nd boolean argument 'DONE' specialized to detect when the divisor has risen past the square root of 'NUM', then recursion terminates.

Page 38: Introduction to C++ Templates Speaker: Bill Chapman, C++ developer, financial company in Mid-Manhattan. Can be reached via: cplusplus@ccjj.info ' This.

betterPrime.cpp#include "betterPrime.h”template <int FROM, int TO>struct ShowPrimes { static void run() { ShowPrimes<FROM, TO – 1>::run(); if (IsPrime<TO>::VAL) std::cout << TO << std::endl;} };template <int END>struct ShowPrimes<END, END> { static void run() { if (IsPrime<END>::VAL) std::cout << END << std::endl;} };int main() { enum { START = 0 }; // max value of START is 3403924 ShowPrimes<START, START + 100>::run();}$ ./a.out23571113. . .

Just like 'simplePrimes.cpp', except includes 'betterPrime.h' instead of 'simplePrime.h' and produces the same output 0 - 100

Page 39: Introduction to C++ Templates Speaker: Bill Chapman, C++ developer, financial company in Mid-Manhattan. Can be reached via: cplusplus@ccjj.info ' This.

Generating Primes in Error Messages

It would be fun to generate primes without creating an executable – that is, nothing gets to be done at run time. In struct 'AssertPrime' we force a compiler error by dividing by 0 if 'NUM' is prime.#include "betterPrime.h"

template <int NUM>struct AssertPrime { enum { VAL = 1 / (0 == IsPrime<NUM>::VAL) };};

template <int FROM, int TO>struct ShowPrimes { enum { A = ShowPrimes<FROM, TO – 1>::VAL, VAL = AssertPrime<TO>::VAL + A };};template <int END>struct ShowPrimes<END, END> { enum { VAL = AssertPrime<END>::VAL };};

enum { VAL = ShowPrimes<1, 100>::VAL };

Notice that <iostream> is never included and there is no 'main'.

Page 40: Introduction to C++ Templates Speaker: Bill Chapman, C++ developer, financial company in Mid-Manhattan. Can be reached via: cplusplus@ccjj.info ' This.

Error Messages from assertPrimes.cpp

Gives rise to a horrid torrent of error messages. But notice 'AssertPrime<N>': assertPrimes.cpp:13:1: error: expected unqualified-id before ‘}’ tokenassertPrimes.cpp: In instantiation of ‘AssertPrime<2>’:assertPrimes.cpp:10:10: recursively instantiated from ‘ShowPrimes<1, 99>’assertPrimes.cpp:10:10: instantiated from ‘ShowPrimes<1, 100>’assertPrimes.cpp:19:32: instantiated from hereassertPrimes.cpp:5:10: warning: division by zero [-Wdiv-by-zero]assertPrimes.cpp:5:10: error: ‘(1 / 0)’ is not a constant expressionassertPrimes.cpp:5:10: error: enumerator value for ‘VAL’ is not an integer constantassertPrimes.cpp: In instantiation of ‘AssertPrime<3>’:assertPrimes.cpp:10:10: recursively instantiated from ‘ShowPrimes<1, 99>’assertPrimes.cpp:10:10: instantiated from ‘ShowPrimes<1, 100>’assertPrimes.cpp:19:32: instantiated from hereassertPrimes.cpp:5:10: warning: division by zero [-Wdiv-by-zero]assertPrimes.cpp:5:10: error: ‘(1 / 0)’ is not a constant expressionassertPrimes.cpp:5:10: error: enumerator value for ‘VAL’ is not an integer constantassertPrimes.cpp: In instantiation of ‘AssertPrime<5>’:. . .

Page 41: Introduction to C++ Templates Speaker: Bill Chapman, C++ developer, financial company in Mid-Manhattan. Can be reached via: cplusplus@ccjj.info ' This.

Grepping Error Output for 'AssertPrime'

$ g++ assertPrimes.cpp 2>&1 | grep ’AssertPrime<’assertPrimes.cpp: In instantiation of ‘AssertPrime<2>’:assertPrimes.cpp: In instantiation of ‘AssertPrime<3>’:assertPrimes.cpp: In instantiation of ‘AssertPrime<5>’:assertPrimes.cpp: In instantiation of ‘AssertPrime<7>’:assertPrimes.cpp: In instantiation of ‘AssertPrime<11>’:assertPrimes.cpp: In instantiation of ‘AssertPrime<13>’:assertPrimes.cpp: In instantiation of ‘AssertPrime<17>’:assertPrimes.cpp: In instantiation of ‘AssertPrime<19>’:assertPrimes.cpp: In instantiation of ‘AssertPrime<23>’:assertPrimes.cpp: In instantiation of ‘AssertPrime<29>’:assertPrimes.cpp: In instantiation of ‘AssertPrime<31>’:assertPrimes.cpp: In instantiation of ‘AssertPrime<37>’:assertPrimes.cpp: In instantiation of ‘AssertPrime<41>’:assertPrimes.cpp: In instantiation of ‘AssertPrime<43>’:assertPrimes.cpp: In instantiation of ‘AssertPrime<47>’:assertPrimes.cpp: In instantiation of ‘AssertPrime<53>’:assertPrimes.cpp: In instantiation of ‘AssertPrime<59>’:assertPrimes.cpp: In instantiation of ‘AssertPrime<61>’:. . .

Page 42: Introduction to C++ Templates Speaker: Bill Chapman, C++ developer, financial company in Mid-Manhattan. Can be reached via: cplusplus@ccjj.info ' This.

Copyright © 2012 – Bill Chapman

No rights reserved – public domain. May be modified, duplicated, sold and / or used for any purpose by

anyone without permission from the author.