12.06.07
再談 C++/CLI: Template Metaprogramming
Template metaprogramming 是 Metaprogramming 的技巧之一, 也是 Modern C++ 中重要的元素
基本上 Template metaprogramming 是利用 compiler 來產生程式碼(不是目的碼喔)的一個方法
至於要產生程式碼, 就有一個問題要考慮, 就是他的能力有多強
然而 Template metaprogramming 已經被證明是 Turing-complete
也就是他能表達所有程式語言能表達的程式(嚴格的說是演算法)
舉一個例子來說 基本上大家舉例都舉 factorial
我在這邊示範一下費伯納西數 費伯納西數得一個簡單的解法是使用遞迴
然而費伯納西數可不是一個 Divide & Conquer 的函式 (反而是 Divide & Complex)
所以不適合用遞迴 我們可以利用 Template metaprogramming 把計算費伯納西數的工作交給編譯器
這樣在執行時間 就不會花任何時間和記憶體(遞迴需要占用Stack Frame)來計算了
基本上 Template metaprogramming 用到了template 中有 template 的技巧
如此一來編譯器會把他遞迴展開 然後再利用特化來製造終止條件
看以下的程式碼
#include <iostream> using namespace std; template <unsigned int n> struct Fibonacci { enum { value = Fibonacci <n-1>::value + Fibonacci <n-2>::value }; }; template<> struct Fibonacci <0> { enum { value = 0 }; }; template<> struct Fibonacci <1> { enum { value = 1 }; }; int main() { cout << Fibonacci<5>::value << endl; }
看到這個程式 如果沒有覺得渾身不對勁 覺得這是邪魔歪道的 而且還覺得超炫
恭喜你 你可以繼續看下去了 Template metaprogramming 將會成為你讓人驚艷的技巧
這邊有一個地方要說明的 就是 enum hack
這是一個製造class 中 static const int 變數的方法
當然上述寫 static const int 也是可以行得通的
只是 大家比較習慣使用 enum hack
Fibonacci<15>::value 會在編譯時期就 計算好數值 610
C#, VB 在2.0 之後 也支持泛型了 那能不能使用 Template metaprogramming ?
基本上是不行的 .NET 的泛型 既不是在Compiler Time展開
也不能接受 non-type template parameter 更不用說有特化的能力
也基本上只有 C++/CLI 能使用 template 來達成這個技巧
然而 managed class 內不能接受 enum hack 的語法 所以要使用 static const int 來完成
下面來看Peter Simons用 Template metaprogramming 用來解質數的例子 請參考 prime-sieve.cc
/* * prime-sieve.cc -- written by Peter Simons <simons@cryp.to> * * This is an example of C++ template meta-programming. The interesting * part is the primeSieve<> meta template, which will generate a list * of integers in the range [2..n] using the Sieve of Erathostenes. */ ////////////////////////////////////////////////// // Meta-programming Infrastructure ////////////////////////////////////////////////// struct NullType { }; template<typename Head, typename Tail> struct Typelist { }; template<unsigned int Val> struct IntType { enum { value = Val }; }; template<bool flag, typename T, typename U> struct Select { typedef T Result; }; template<typename T, typename U> struct Select<false, T, U> { typedef U Result; }; ////////////////////////////////////////////////// // Alorithm to create [i..n] ////////////////////////////////////////////////// template<unsigned int i, unsigned int n> struct makeIntList { typedef Typelist< IntType<i>, typename makeIntList<i+1, n>::Result > Result; }; template<unsigned int n> struct makeIntList<n, n> { typedef Typelist< IntType<n>, NullType > Result; }; ////////////////////////////////////////////////// // The Sieve of Erathostenes ////////////////////////////////////////////////// template<unsigned int i, typename TList> struct sieveOne; template<unsigned int i, typename x, typename xs> struct sieveOne< i, Typelist<x,xs> > { typedef typename Select< x::value % i != 0, Typelist< x, typename sieveOne<i, xs>::Result >, typename sieveOne<i, xs>::Result >::Result Result; }; template<unsigned int i> struct sieveOne<i, NullType> { typedef NullType Result; }; template<typename TList> struct sieveAll; template<typename x, typename xs> struct sieveAll< Typelist<x,xs> > { typedef Typelist< x, typename sieveAll<typename sieveOne<x::value, xs>::Result>::Result > Result; }; template<> struct sieveAll<NullType> { typedef NullType Result; }; template<unsigned int n> struct primeSieve { typedef typename sieveAll<typename makeIntList<2,n>::Result>::Result Result; }; // 省略把 Algorithm 包裝成 Interator 的部分 ... // TEST #include <iostream> #include <iterator> using namespace std; int main(int, char**) { enum { n = 20 }; cout << "Finding prime numbers in [2.." << n << "]:\n"; toIterator<primeSieve<n>::Result > generator; generator(ostream_iterator<int>(cout, " ")); cout << "\n"; return 0; } // Result Finding prime numbers in [2..20]: 2 3 5 7 11 13 17 19
簡潔有力 效率高