One frequently used feature of C++ is templates. Templates can be applied to classes and functions to make your work more generic without sacraficing runtime performance. It’s a fantastic feature that brought with it one unexpected technology: template metaprogramming.

Template metaprogramming essentially means doing some of your computation at compile time by taking advantage of the way templates are used in C++. This could be done to improve your program’s runtime performance (in exchange for slower compile times) or simply to make the programmer’s work easier.

Take this code as an example:

// Generic exponent compile-time calculations. Pow<2,3>::result == 2^3
template <unsigned long B, unsigned long E>
struct Pow
{
    static const unsigned long result = B * Pow<B, E-1>::result;
};

template <unsigned long B>
struct Pow<B, 0>
{
    static const unsigned long result = 1;
};

This code will allow you to calculate exponents at compile time, provided you also know the base and exponent at compile time. This recursive algorithm is computed during compilation to provide a constant value for result. The template specialization on E = 0 is the exit point for the algorithm; without this the algorithm will never complete.

Sure, if the programmer knows the exponent which needs to be calculated, he or she could calculate this themselves and write the value in the code. However, sometimes it’s clearer and/or easier to show the calculation in your code. Or you may want to do something more complex.

Take for example the slightly longer example below, which builds on our Pow<B, E> example above:

#include <iostream>
#include <limits>

// Generic exponent compile-time calculations. Pow<2,3>::result == 2^3
template <unsigned long B, unsigned long E>
struct Pow
{
    static const unsigned long result = B * Pow<B, E-1>::result;
};

template <unsigned long B>
struct Pow<B, 0>
{
    static const unsigned long result = 1;
};

// Bailey-Borwein-Plouffe formula for calculating pi
// http://en.wikipedia.org/wiki/Bailey-Borwein-Plouffe_formula
template <unsigned long N>
struct CalculatePi
{
    static const double pi =
    (
        1.0/Pow<16,N>::result *
        (
            4.0/(8*N + 1.0) - 2.0/(8*N + 4.0) -
            1.0/(8*N + 5.0) - 1.0/(8*N + 6.0)
        )
    ) + CalculatePi<N-1>::pi;
};

template <>
struct CalculatePi<-1>
{
    static const double pi = 0;
};

// main program. Print pi, calculated from 10 iterations of
// the BBP formula above
int main()
{
    std::cout.precision(std::numeric_limits<double>::digits10);
    std::cout << "pi: " << CalculatePi<10>::pi << std::endl;

    return 0;
}

This will print:

pi: 3.14159265358979

This complete program prints the value of π, calulated from 10 iterations of the following formula:

Bailey-Borwein-Plouffe formula for calculating pi

All calculations are done at compile time; at runtime the program simply reads the precalculated value from memory and prints it. Calculating the value with more iterations will slow down the compilation, but will not affect the runtime performance. There are other things which can be done with template metaprogramming, such as calculating typenames, which I will cover in a later post.

  1. André says:

    Hi Tim,
    Nice blog.
    When I run your code in Microsoft Visual C++ 2010 Express I get:
    … error C2864: ‘CalculatePi::pi’ : only static const integral data members can be initialized within a class
    Ho to repair this?
    Regards,
    Andre

  2. Maven says:

    Instead

    static const double pi =

    write

    static constexpr double pi =

    some new compiler thing.