2016-10-09 11 views
7

いくつかの設計方法に基づいて出力を生成するプロトタイプ動作がしばしばあります。私は、私が必要とする多くの機能を可能にする設計方法をテンプレートします。しかし、時にはデザインメソッドが実行時に与えられることがあるので、私は通常、大きなswitch文を書く必要があります。この方法の主な欠点は1つがサポートされているすべての列挙を入力し、関係しているということである - 私は誰もがこのシステムのための素晴らしいパターンを考え出したかどうかについての好奇心実行時パラメータに基づいて整数のテンプレート関数を実行

enum class Operation 
{ 
    A, B 
}; 


template<Operation O> 
    void execute(); 

template<> 
    void execute<A>() 
    { 
     // ... 
    } 

template<> 
    void execute<B>() 
    { 
     // ... 
    } 

void execute(Operation o) 
{ 
    switch (o) 
    { 
    case Operation::A: return execute<Operation::A>(); 
    case Operation::B: return execute<Operation::B>(); 
    } 
} 

:これは通常、このようになります新しい列挙が実装されている場合は、いくつかの場所をメンテナンスします。

e:コンパイル時のテンプレートを使いこなす理由は、HPCのインラインメソッドとconstexprプロパティを継承できるようにすることです。

e2:実際には、暗黙のスイッチ構造を使用して、すべての可能なコードパスをコンパイラに生成させることを求めていると思います。多分再帰的テンプレートマジック?

+0

継承と多型の使用はどうですか? –

+0

私が追加したばかりのように、コンパイラはコンパイル時にインライン化と最適化を行うことが非常に重要です(つまり、コード構造全体が可視で確定的です)。そうでなければ、仮想関数は明らかに問題を解決します。 – Shaggi

答えて

4

このタスクに実際にテンプレートを使用する場合は、this oneと同様のテクニックを使用できます。

// Here second template argument default to the first enum value 
template<Operation o, Operation currentOp = Operation::A> 
// We use SFINAE here. If o is not equal to currentOp compiler will ignore this function. 
auto execute() -> std::enable_if<o == currentOp, void>::type 
{ 
    execute<currentOp>(); 
} 

// Again, SFINAE technique. Compiler will stop search if the template above has been instantiated and will ignore this one. But in other case this template will be used and it will try to call next handler. 
template<Operation o, Operation currentOp = Operation::A> 
void execute() 
{ 
    return execute<o, static_cast<Operation>(static_cast<int>(currentOp) + 1)(c); 
} 
+0

興味深い(リンクは壊れています)、私はそれをチェックします – Shaggi

+0

私はリンクを修正しました。 –

2
template<class F, std::size_t...Is> 
void magic_switch(std::size_t N, F&& f, std::index_sequence<Is...>){ 
    auto* pf = std::addressof(f); 
    using pF=decltype(pf); 
    using table_ptr = void(*)(pF); 
    static const table_ptr table[]={ 
    [](pF){ std::forward<F>(*pf)(std::integral_constant<std::size_t, Is>{}); }... 
    }; 
    return table[N](pf); 
} 
template<std::size_t Count, class F> 
void magic_switch(std::size_t N, F&& f){ 
    return magic_switch(N, std::forward<F>(f), std::make_index_sequence<Count>{}); 
} 

これは、実行時定数に基づいてエントリコンパイル時定数、ピッキングにラムダを呼び出すジャンプテーブルを作ります。 switch case文をコンパイルする方法とよく似ています。

void execute(Operation o) { 
    magic_switch<2>(std::size_t(o), [](auto I){ 
    execute<Operation(I)>(); 
    }); 
} 

voidを返さないように変更することはできますが、すべてのブランチは同じ型を返す必要があります。

関連する問題