2017-11-07 13 views
3

私は、コンパイラは、関数の8つの異なる(うまくいけば重く最適化された)バージョンを作成したいので、この最善の方法は、

template<bool switch1, bool switch2, bool switch3> 
void foo(){ 
} 

のような機能が、私はこのような関数を定義しています。しかし、関数を呼び出すときに、私はこのような何かをしなければなりません。

inline void call_foo(bool switch1, bool switch2, bool switch3){ 
    if (switch1 && switch2 && switch3) 
     foo<true, true, true>(); 
    else if (!switch1 && switch2 && switch3) 
     foo<false, true, true>(); 
    // 6 more ifs 
} 

これを行うもっとエレガントな方法はありますか?

注:私はテンプレートの引数をスキップすることができます。しかし、コードの2つのバージョンをプロファイリングすると、テンプレート化されたコードでは速度が大幅に向上することがわかりました。

マップはエレガントないくつかの定義については、よりエレガントになり
+0

表現SFINAEおそらく[インラインリンク](http://en.cppreference.com/w/cpp/language/sfinae) –

+0

"テンプレート化されたコードで大幅な改善"私はスピードの違いを引き起こす別の何かがあると思う。あなたが実行しているコードではテンプレートはありません... – user463035818

+0

プロファイリング時にOPTIMIZEDビルド( '-O2'コンパイラオプションなど)を使用していて、非テンプレート関数を' inline'として定義していますか?そのコード)? –

答えて

3

あなたは可変引数テンプレートのような、あなたはそれをこのように実装することができた場合:マッピングする

template<bool ...Args> 
struct dispatcher 
{ 
    static void call(){ 
     foo<Args...>(); 
    } 

    template<class ...Args1> 
    static void call(bool b, Args1... ar1){ 
     if (b) 
      dispatcher<Args..., true>::call(ar1...); 
     else 
      dispatcher<Args..., false>::call(ar1...); 
    } 

}; 

void call_foo(bool a, bool b, bool c) 
{ 
    dispatcher<>::call(a,b,c); 
} 
+0

次のディスパッチャを 'dispatcher '(または 'false')と呼びます。 'false、Args ... 'を呼び出すと、' call_foo'の引数の順序を逆にする必要があります。特別な提案: 'call()'メソッドが 'static'の場合、' dispatcher'型のオブジェクトを作成する理由はありません。あなたは単に 'dispatcher <...> :: call(...を呼び出すことができます); ' – max66

+0

@ max66提案してくれてありがとう、ただそれを修正しました。 –

+0

私は他の答えを好むバットを好む:これは、ブーリアットのテンプレート値のバリデーシックなリストを持つ仮説' foo <> +1。 – max66

3

std::map<std::array<bool,3>, void(*)()> dispatch { 
    {{false, false, false}, foo<false, false, false>}, 
    /// 
    {{true, true, true}, foo<true, true, true>} 
}; 

std::array<bool, 3> to{ {switch1, switch2, switch3} }; 

dispatch[to](); 
+0

OPが最大のパフォーマンスを追求しているようです。これはそれを達成する方法ではありません。 –

+1

@JohnZwinck - (1)OPはそれをテンプレート化するときに 'foo'自体のパフォーマンスが向上したと述べました。私は彼らが "最大"の性能を追求しているとは思っていませんでした。 (2)OPはこれと 'std :: unordered_map'で再生できます。目標が達成されたかどうかを知るためにはプロファイル作成が必要です。 (3)彼らは優雅さを求めた。 – StoryTeller

3

同様に、あなたが可能配列を使用します。

const std::array<void (*)(), 8> dispatch { 
    &foo<false, false, false>, &foo<false, false, true>, 
    &foo<false, true, false>, &foo<false, true, true>, 
    &foo<true, false, false>, &foo<true, false, true>, 
    &foo<true, true, false>, &foo<true, true, true>, 
}; 

dispatch[switch1 << 2 | switch2 << 1 | switch3](); 
+0

実際にナイーブなマップよりも優れています。おそらく最小限に抑えることができます。 – StoryTeller

0

Jarod42の検索-TA BLEソリューションは、おそらく最速かつ最も簡単ですが、ちょうど完全のために、元のコードの多かれ少なかれ逐語的に交換が余分index_sequenceと

template< std::size_t I, std::size_t... Is > 
void call_foo(bool switch1, bool switch2, bool switch3, std::index_sequence<I,Is...>) 
{ 
    if(switch1 == bool(I&1) && switch2 == bool(I&2) && switch3 == bool(I&4)) 
    foo<bool(I&1),bool(I&2),bool(I&4)>(); 

    if constexpr(sizeof...(Is) > 0) 
    call_foo(switch1, switch2, switch3, std::index_sequence<Is...>{}); 
} 

// to be used as 

call_foo(true, false, true, std::make_index_sequence<2*2*2>{}); 

かもしれないが、これはまた、任意のboolsに数える一般化することができます。

template< class F, class... T, std::size_t... Js, std::size_t I, std::size_t... Is > 
void untemplatize_impl(F&& f, std::index_sequence<Js...> bits, std::index_sequence<I,Is...>, T... bools) 
{ 
    if(I == ((unsigned(bools)<<Js) | ...)) 
    std::forward<F>(f).template operator()<bool(I&(1<<Js))...>(); 

    if constexpr(sizeof...(Is) > 0) 
    untemplatize_impl(std::forward<F>(f), bits, std::index_sequence<Is...>{}, bools...); 
} 

template< class F, class... T > // SFINAEd, if needed 
void untemplatize(F&& f, T... bools) 
{ 
    untemplatize_impl(std::forward<F>(f), std::make_index_sequence<sizeof...(T)>{}, std::make_index_sequence<(1u<<sizeof...(T))>{}, bools...); 
} 

// to be used as 

untemplatize(foo{}, false, true, true); 

だけでなく、C++ 11で可能であることと同じことを、しかし、はるかに厄介:ファンクタを取る

完全に汎用的なC++ 17のバージョンは、のように見えることがあります。