C++元操作的由來和工作原理,程序Bug的意外收穫 #知識青年#

我是一直使用C語言寫嵌入式程序的,最近看了一下STM32的觸摸屏輔助工具touchGFX,其輸出代碼是C++,感受到了 C++在模塊化代碼生成中的優勢。(我會陸續寫一些關於 C++的文件,主要結合touchGFX工具,從嵌入式的角度看 C++的學習應用)。

元操作的由來

今天講的元操作是基於類型的操作運算,而不是基於數值的運算,這點和普通程序代碼有很大不同。問題起始於1994年,Erwin Unruh在一次C++會議上展示了一段不能編譯的程序代碼,這可能是最著名的一段無法編譯的Erwin Unruh代碼。

<code>template  struct D
{ D(void*); operator int(); };

template struct is_prime {
enum { prim = (p%i) && is_prime 2 ? p : 0), i -1> :: prim };
};

template < int i > struct Prime_print {
Prime_print a;
enum { prim = is_prime::prim };
void f() { D d = prim; }
};

struct is_prime<0,0> { enum {prim=1}; };
struct is_prime<0,1> { enum {prim=1}; };
struct Prime_print<2> { enum {prim = 1}; void f() { D<2> d = prim; } };
#ifndef LAST
#define LAST 10
#endif
main () {
Prime_print<last> a;
} /<last>
/<code>

Erwin Unruh使用元操作編譯器,但是顯示這是無效 C++代碼。為什麼這段代碼如此著名,請看下面的編譯結果。

C++元操作的由來和工作原理,程序Bug的意外收穫 #知識青年#

編譯結果

請注意紅色部分代碼結構。 該程序在編譯的過程中,計算出了前30個質數。這意味著模版實例可以在編譯過程中完成計算,而且模版元操作是Turing-complete的(即狀態自動轉換),因此可以用於解決計算問題(當然,這裡也要考慮到遞歸計算的深度問題,在C++11裡至少可達到1024次遞歸計算)。

元操作工作原理

我們看一段階乘的運算代碼

<code>#include <iostream>

template // (2)
struct Factorial{
static int const value = N * Factorial::value;
};

template <> // (3)
struct Factorial<1>{
static int const value = 1;
};

int main(){
std::cout << std::endl;
std::cout << "Factorial<5>::value: " << Factorial<5>::value << std::endl; // (1)
std::cout << "Factorial<10>::value: " << Factorial<10>::value << std::endl;
std::cout << std::endl;
}
/<iostream>/<code>

在(1)處調用 factorial<5>::value實現了對 (2)處模版的實例化,在初始化的過程中Factorial<4>::value也被實例化。這種遞歸一直運算直到 template Factorial<1> ,滿足 (3)處條件結束,看下圖

C++元操作的由來和工作原理,程序Bug的意外收穫 #知識青年#

遞歸過程

程序輸出結果

C++元操作的由來和工作原理,程序Bug的意外收穫 #知識青年#

結果

那如何看到這結果是編譯時運算的呢?

C++元操作的由來和工作原理,程序Bug的意外收穫 #知識青年#

C++元操作的由來和工作原理,程序Bug的意外收穫 #知識青年#

上圖加深語句的反彙編代碼

可以看到,factorial<10>直接就是一個常數3628800賦值,因此可判斷是在編譯過程中計算出來的。

元操作雖然可以極大的提高程序運算效率,但是不建議大量使用,在嵌入式系統中適當的使用,卻能極大的提高系統運行效率。在touchGFX裡,使用到了typelist和元件操作,但是不是C++的庫文件,是touchGFX裡自定義的庫文件,可以進一步提高效率,縮減代碼。

謝謝關注,轉發。


分享到:


相關文章: