2017-03-06 14 views
0

最終的なバイナリにマージされるテンプレート化されたクラスのメンバ関数を持つ方法はありますか?私は、特定の機能をサポートし、それに依存して、メンバ関数の内部いくつかの追加のステップを必要とすることができるクラスがある:g ++:テンプレートメンバ関数のインスタンスをマージする

template <uint32_t features> 
class Driver { 
    static bool set (uint32_t value) { 
     /* do something required for every feature */ 
     if (features & Feature::A) 
     /* do special things if Feature::A */ 
     /* do something required for every feature */ 
     if (features & Feature::B || features & Feature::C) 
     /* do something special for either Feature::B or Feature::C */ 
     return true; 
    } 
    /* more, similar methods */ 
}; 

後のコードで、私は、実際に応じDriver<Feature::A>::set又はDriver<Feature::A | Feature::B>::set等のコールを使用します利用可能な機能。これまでのところうまくいきますが、g++-5.4.0 -std=c++11 -O3でコンパイルされたコードだけが同じメソッドをどのようにマージしていないのですか?このクラスには、共通の共通部分を持つ複数の関数があり、実際には最終的なバイナリのサイズが大きくなります。共通する部分についてはプライベート関数を使用すると、サイズのオーバーヘッドが減少しますが、私の意見では読むことのように友好的ではありませんが:特別のひとつに「早く出て」パスがある場合にも、これは、問題を紹介

template <uint32_t features> 
class Driver { 
    static void _set_common_0 (value) { 
     /* do something required for every feature */ 
    } 
    static bool set (uin32_t value) { 
     _set_common_0 (value); 
     if (features & Feature::A) 
      _set_special_0 (value); 
     _set_common_1 (value); 
     if (features & Feature::B || features & Feature::C) 
      _set_special_1 (value); 
     return true; 
    } 
}; 

以前は単純なreturnで行うことができましたが、現在はサブ機能からエスカレートされ、そのサブ機能の戻り値に応じてさらにチェックが行われます。

私は何をしたいですか?コンパイラはDriver<Feature::B>::setDriver<Feature::C>::setのシンボルを(使用時に)発行しますが、コードで同一のコード位置を指していることを確認してください。どのように私はこれを行うことができる任意のアイデア? (好ましくは、新しい規格から機能を必要とC++ 11に滞在していない)

EDIT:(テンプレートインスタンスでここで生産)の同一の命令シーケンスをgccによって結合されていない理由を明確にするために、私は、疑問に思って。私の理解のために、-ftree-tail-mergeは少なくともそれらを同じ実装/コードシーケンス(e.q. Driver<Feature::B>::setDriver<Feature::C>::set)へのジャンプで置き換えるべきです。

+1

本当にクラステンプレートが必要ですか?これはテンプレートの使用方法ではないため、実行時にコンパイル時定数をチェックしています。 'if(features&Feature :: A)' –

+0

は本当にコンパイル時定数ではありません。使用すべき機能は実行時依存であり、コンパイル時にはわかりません。実行時環境に依存して、私は 'Feature :: A'を使うことができますし、使用することはできません。 'Driver :: set'呼び出しは、直接発行されるのではなく、環境の検出された機能に依存して初期化中に割り当てられる関数ポインタによって呼び出されます。 – Jonas

答えて

0

投稿したコードから、あなたがテンプレートを誤って使用していることがわかります。正しい方法はDriverの部分的な特殊化を定義することです:

template <uint32_t features> 
class Driver; // There may be some default implementation, but it is not necessary 

// Specialize for feature A only 
template <> 
class Driver<Feature::A> { 
    bool set (uint32_t value) { 
     /* do special things for Feature::A */ 
     /* do something required for every feature */ 
     return true; 
    } 
}; 

// Specialize for feature B only 
template <> 
class Driver<Feature::B> { 
    bool set (uint32_t value) { 
     /* do special things for Feature::B */ 
     /* do something required for every feature */ 
     return true; 
    } 
}; 

// Specialize for features A and B 
template <> 
class Driver<Feature::A | Feature::B> { 
    bool set (uint32_t value) { 
     /* do special things for Feature::A */ 
     /* do special things for Feature::B */ 
     /* do something required for every feature */ 
     return true; 
    } 
}; 

テンプレートアプローチの利点は、特定の機能が有効になっているかどうかをチェックするため、実行時のオーバーヘッドを持っていないということです。もちろん、多くの機能がある場合は、多数の機能の組み合わせが存在します。したがって、出力バイナリの膨れ。そのため、クラステンプレートを取り除き、ランタイム関数の引数を使用することをお勧めします。たとえば、コンストラクタにフィーチャを渡します。

class Driver { 
private: 
    uint32_t features_; 

public: 
    Driver(uint32_t features) : features_(features) {} 
    bool set (uint32_t value) { 
     /* do something required for every feature */ 
     if (features_ & Feature::A) 
     /* do special things if Feature::A */ 
     /* do something required for every feature */ 
     if (features & Feature::B || features & Feature::C) 
     /* do something special for either Feature::B or Feature::C */ 
     return true; 
    } 
}; 
+0

私はあなたの最初の解決策の利点を見ていません。定数の折り畳み後、テンプレートのインスタンス化のコードは同じになります(ドローバックでは、共通パートはソリューションごとに複数回(各ボディ内で)記述する必要があります)。将来の変更により、問題/エラーを招く可能性のある1つの本体が欠落する可能性があります。 テンプレートは、実行時のオーバーヘッドを削減するために使用されます。各呼び出しで使用できる機能がわかっています。私は最初に正しいコントロールフローを選択する*チェックを*持っています。 問題は、なぜ 'gcc'が同じコード部分をマージしないのかということです。 – Jonas

+0

あなたは正しいです。そのため、ここでテンプレートを用意する必要はなく、2番目のソリューションを使用する必要があります。テンプレートを*使用する必要があると判断した場合、そのテンプレートの使用方法の例を示しました。 –

+0

2番目のソリューションでは、必須ではないランタイムチェックを導入します。私はすでに、私が利用できる機能を呼び出すたびにコンパイル時にこれを行うことができるので、これは可能であるべきです(あるいは、そうでなければなりません)。残りの質問は、 'gcc'を同じコードを生成するテンプレートインスタンスをどのように組み合わせるかです。例えば。 'Driver :: set'と' Driver :: set'は同じですが、2回発生します。 あなたの言うところではテンプレートを使用する必要があります:私のソリューションと比較してあなたのソリューションのメリットは何ですか? – Jonas

関連する問題