Friday, March 28, 2008

Template Metaprogramming

We must have seen that C++ has incorporated template metaprogramming.
It also mentions that it gets calculated at compile time ( Wow! ).
This saves the execution time of a program which is great.
So lets see the standard example give in C++ Templates book by Vandevoorde/Josuttis.

template <int N>
class Pow3 {
public:
enum { result = 3 * Pow3<N-1>::result };
};

template <>
class Pow3<0> {
public:
enum { result = 1 };
};

Now when you write the program like :

int main() try {
cout << " 3 to power 3 is : " << Pow3<3>::result << endl;
return 0;
} catch(...) {
cout << "Uncaught exception..." << endl;
}

It prints : 27.
Conceptually enums are constants and constants are to be given value at compile time only.
Therefore, we can assume that Pow3<3>::result was having 27 as value.
But how will you prove that compiler did the actual calculation for you ???
If you are not sure check out the reply for this blog.

1 comment:

Siddhartha Singh said...

Could not resist to provide the answer myself.

If you see it carefully, it is a recursive call to the class Pow3 <N-1> .
Since every successful recursion has to have a terminating condtion.
We have a specialized template class with the same name. template <> class Pow3<0>. This class ends the recursion.
So we will do a trick. Change the scope of the enum of this specialized class as private. Then when compiler will get to the N's value as 0, it will try to access the result of specialized class. There it would get an access error.
And then it will throw the stack trace of its own which is where you get the proof of this metaprogramming.
Lets check out the output :

Invoking: GCC C++ Compiler
g++ -O0 -g3 -Wall -c -fmessage-length=0 -MMD -MP -MF"A.d" -MT"A.d" -o"A.o" "../A.cpp"
../A.cpp: In instantiation of ‘Pow3<1>’:
../A.cpp:21: instantiated from ‘Pow3<2>’
../A.cpp:21: instantiated from ‘Pow3<3>’
../A.cpp:34: instantiated from here
../A.cpp:27: error: ‘Pow3<0>::<anonymous enum> Pow3<0>::result’ is private
../A.cpp:21: error: within this context


Here you see it is recursive call which throws an error for Pow3<0>::result.

So you got the reason. isn't it?