Professional C__ - Marc Gregoire [359]
Factorial at Compile Time
Template metaprogramming allows you to perform calculations at compile time instead of at run time. The following code is a small example that calculates the factorial of a number at compile time:
template class Factorial { public: static const long long val = (f*Factorial }; template<> class Factorial<1> { public: static const long long val = 1; }; int main() { cout << Factorial<6>::val << endl; return 0; } Code snippet from Factorial\Factorial.cpp This will calculate the factorial of 6, mathematically written as 6!, which is 1×2×3×4×5×6 or 720. The code is using template recursion as explained earlier in this chapter, which requires the general recursive template and the base template to stop the recursion. It is important to remember that the factorial calculations are happening at compile time. At run time you access the compile time calculated value through ::val, which is just a constant value. Loop Unrolling A second example of template metaprogramming is to unroll loops at compile time instead of executing the loop at run time. Take a look at the following example: template class Loop { public: static inline void Do(FuncType func) { Loop func(i); } }; template class Loop<-1, FuncType> { public: static inline void Do(FuncType func) { } }; Code snippet from LoopUnrolling\LoopUnrolling.cpp This example uses template recursion because it needs to do something in a loop at compile time. For template recursion you need the recursive implementation of the template and a base template that will stop the recursion. On each recursion, the Loop template will instantiate itself with i-1. When it hits -1, the base template is used, which stops the recursion. The Loop template can be used as follows: void DoWork(int i) { cout << "DoWork(" << i << ")" << endl; } int main() { Loop<3, function } Code snippet from LoopUnrolling\LoopUnrolling.cpp This code will cause the compiler to unroll the loop and will call the function DoWork() four times in a row (0-3). Note that this example is using the C++11 std::function feature, which is explained in Chapter 16. The output of the program is: DoWork(0) DoWork(1) DoWork(2) DoWork(3) This already looks interesting, but we can do better, and make the call even easier to write by using decltype: Loop<3, decltype(DoWork)>::Do(DoWork); Code snippet from LoopUnrolling\LoopUnrolling.cpp This code will output exactly the same as the first version. However, here you ask the compiler to deduce the type of the DoWork() function automatically by using the decltype keyword so that you don’t need to write the exact type yourself. To further demonstrate the power of the new C++11 features in combination with template metaprogramming, take the following call for the Loop template: double DoWork2(string str, int i) { cout << "DoWork2(" << str << ", " << i << ")" << endl; return 0.0; } int main() { auto a = bind(DoWork2, "TestStr", placeholders::_1); Loop<2, decltype(a)>::Do(a); } Code snippet from LoopUnrolling\LoopUnrolling.cpp This is using quite a number of C++11 features. The code first implements a function that accepts a string and an int and returns a double. The main() function uses std::bind() to bind the first parameter of DoWork2() to a fixed string, "TestStr". See Chapter 13 for details on std::bind(). The result of std::bind() is assigned to a, and because of the auto keyword, the compiler will automatically deduce the type of a. The code then instantiates the Loop template and uses decltype to let the compiler deduce the type of a and use it as the second template parameter. If you compile and run this code, the output should be as follows: DoWork2(TestStr, 0) DoWork2(TestStr, 1) DoWork2(TestStr, 2) You can go one step further and remove the need to specify the type of the function as the second parameter for the Loop