2012-02-19 65 views
2

如果我有一個包含一堆其他代碼的模板。 g ++是否會重新生成對於每個版本的模板都相同的所有代碼?g ++模板實例化器有多聰明(避免模板膨脹)

例如:

template <typename> T 
T parseSomething(const std::string& data) { 
    // Some state variables go here 
    enum State {state1,state2,state3} state; 
    for(std::string::const_iterator i=data.begin();i!=data.end();++i) { 
     // Some big testy stuff to see if we got in the right place 
     switch (state) { 
      case state1: { 
       switch (*i) { 
        case f: // ... 
     // ... lots of switchy stuff here .. 
     return T(*i); 
    } 
} 

所以在這FUNC ..真正需要的模板的唯一位是返回T(* I)線。

假設我用4個不同的Ts例化它。

parseSomething<float>(data); 
parseSomething<int>(data); 

會克++生成所有其它代碼(環和開關部件),用於每T單獨的時間?或者它會足夠聰明,只生成開關和循環一次..然後爲每個T ..生成返回T(* i);線?

我嘗試過使用-O0進行測試,但它確實複製了各處的開關,但是-O2和以上版本很難說;它似乎是聰明..但它是如此聰明,我無法破譯ASM :)


這是我想要使用來測試我的示例程序。

編譯:

g++ -std=c++0x -fverbose-asm -ggdb3 -fvar-tracking-assignments -O6 -march=native codegen.cpp 

運行:

gdb --args ./a.out asdf1111 

偏執代碼版本:

#include <iostream> 
#include <string> 

using namespace std; 

char getSomething(const string& myString) { 
    for(auto myPlase=myString.begin();myPlase!=myString.end();++myPlase) { 
     if (*myPlase == 'f') { 
      return *(myPlase+1); 
     } 
    } 
} 

template <typename T> 
T getSomething(const string& myString) { 
    return T(getSomething(myString)); 
} 

int main(int argc, char** argv) { 
    string base = argv[1]; 
    float myFloat = getSomething<float>(base); 
    int myInt = getSomething<int>(base); 
    char myChar = getSomething<char>(base); 
    //string newString = getSomething<string>(base); 
    cout << myFloat << " " << myInt << " " << myChar << endl; 
} 

代碼版本我想使用:

#include <iostream> 
#include <string> 

using namespace std; 

template <typename T> 
T getSomething(const string& myString) { 
    for(auto myPlace=myString.begin();myPlace!=myString.end();++myPlace) { 
     if (*myPlace == 'f') { 
      return T(*(myPlace+1)); 
     } 
    } 
} 

int main(int argc, char** argv) { 
    string base = argv[1]; 
    float myFloat = getSomething<float>(base); 
    int myInt = getSomething<int>(base); 
    char myChar = getSomething<char>(base); 
    //string newString = getSomething<string>(base); 
    cout << myFloat << " " << myInt << " " << myChar << endl; 
} 
+0

在現實世界中,我使用ragel生成代碼,它大約有1200行。我想將它變成一個模板函數,主模板位是返回類型,但我不希望看到爲每種類型重新生成這些1200行。 – matiu 2012-02-19 05:17:31

+1

如果你是GCC,你將如何生成有問題的代碼? – 2012-02-19 06:31:35

+1

當然真正的問題是:在偏執的代碼示例中,調用「getsomething」內聯:)? – 2012-02-19 11:04:02

回答

4

我不認爲編譯器足夠聰明來合併獨立於模板參數的代碼。換句話說,該函數將被實例化4次,每個T一次。

在linux上,您可以在生成的目標文件上使用readelf -s來轉儲公共符號,並使用readelf -S來轉儲節;每個非內聯非靜態函數在符號表中都有一個(錯位)條目。 AFAIK,模板instatiations每個都在它自己的部分,以便他們可以在鏈接時合併。

+0

謝謝@zvrba ..是啊,麻煩是我打開任何東西> = -O1它將整個內容都包含進來,而且ASM看起來非常複雜,所以我不能真正分辨出它是否取出了循環,切換東西。我傾向於它可能不會把它拿出來;並與我認爲偏執狂的代碼。 – matiu 2012-02-19 07:34:44

1

在模板內部有一大塊非參數化代碼是不常見的,無論它是一個類還是一個函數。

檢測不依賴參數的大塊代碼聽起來並不困難。引擎只需要在相關的AST節點中記錄一些子樹大小的度量,以及是否有任何子節點是模板參數。

但是,您建議的優化本質上需要將內部作用域與外部作用域分開,這意味着將其重構爲新函數。如果不是臨時的,你有一個命名變量,其生命期包括內部switch?堆棧將被重新安排,內部作用域將依賴於變量,儘管可能沒有引用它,局部變量將不得不作爲參考參數傳遞給switch。這將是一個脆弱,複雜的優化。

如果模板膨脹是一個問題,我會認真推薦「偏執狂」版本,它將模板關注分離爲包裝函數。這樣的包裝不應該很複雜,因爲重點是避免膨脹!

如果薈萃膨脹是一個問題(我剛剛看了你正在使用一個代碼生成和擔心的數千個這樣的模板包裝的源代碼大小),你可能會考慮改變界面一點:

template< typename T, char (*func)(std::string const &) > 
T get_anything(std::string const &s) { 
    return T(func(s)); 
} 

這樣,可以有很多get_something()函數,它們都可以用作get_anything的第二個模板參數。您也可以使用指向成員的指針而不是函數指針作爲模板參數。