2012-02-19 1 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); 

は、G ++のすべての他のコード(ループ及びスイッチ部)毎にTに対して別々の時間を生成しますか?

または、スイッチとループを生成するだけで十分です。それから、各Tに対して... return 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

+1

GCCだったら、問題のコードをどのように生成しますか? –

+1

もちろん、実際の質問は:妄想のコードの例では、 'getsomething'の呼び出しが:) –

答えて

4

私は、コンパイラがテンプレートパラメータとは無関係のコードを融合するのに十分スマートではないと思います。言い換えれば、関数はTごとに1回、4回インスタンス化されます。

Linuxでは、結果のオブジェクトファイルにreadelf -sを使用してパブリックシンボルをダンプし、readelf -Sをダンプセクションに使用できます。各非インライン非静的関数は、シンボルテーブルに(マングルされた)エントリを持ちます。 AFAIK、テンプレートの導入は、リンク時に合体できるように、それぞれ独自のセクションで実行されます。

+0

ありがとうございます@zvrba ..ええ、問題は私が何かをオンにするとすぐです=> -O1それは全体の多くをインラインし、ASMは本当に複雑に見えるので、私は本当にそれがループを取り除いていると言うことができませんスイッチのもの。私はそれに向かって傾いているので、おそらくそれを取っていません。私は思うように、妄想のコードを持って行く。 – matiu

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の2番目のテンプレート引数として使用できます。テンプレートポインタとして、関数ポインタの代わりにポインタへのポインタを使用することもできます。