2011-08-17 15 views

答えて

12

テンプレートを扱うときに、コンパイル時と実行時を橋渡しする典型的な 'トリック'は、バリアント型の訪問です。これは、一般的なイメージライブラリ(Boost.GILまたはスタンドアローンとして利用可能)が、例えば実行するものです。それは、典型的には、の形式をとります。

typedef boost::variant<T, U, V> variant_type; 
variant_type variant = /* type is picked at runtime */ 
boost::apply_visitor(visitor(), variant); 

visitorは、単に転送テンプレートへのポリモーフィックファンクタです:

struct visitor: boost::static_visitor<> { 
    template<typename T> 
    void 
    operator()(T const& t) const 
    { foo(t); } // the real work is in template<typename T> void foo(T const&); 
}; 

これは、タイプのリストは、そのテンプレートが/ことができます素敵なデザインを持っています(ここでは、variant_type型シノニム)は、コードの残りの部分に結合されていません。 boost::make_variant_overのようなメタ機能は、使用するタイプのリストに対する計算も可能にする。

このテクニックは非タイプのパラメータでは使用できないため、手動で訪問をアンロールする必要があります。残念ながら、コードは読み込み可能/保守可能ではありません。

void 
bar(int i) { 
    switch(i) { 
     case 0: A<0>::f(); break; 
     case 1: A<1>::f(); break; 
     case 2: A<2>::f(); break; 

     default: 
      // handle 
    } 
} 

上記スイッチの繰返しに対処する通常の方法は、(AB)がプリプロセッサを使用することです。より良い

#ifndef LIMIT 
#define LIMIT 20 // 'reasonable' default if nothing is supplied at build time 
#endif 
#define PASTE(rep, n, _) case n: A<n>::f(); break; 

void 
bar(int i) { 
    switch(i) { 
     BOOST_PP_REPEAT(LIMIT, PASTE, _) 

     default: 
      // handle 
    } 
} 

#undef PASTE 
#undef LIMIT 

PASTEどちらにも損はない)LIMITのための良い、自己文書名を見つけ、そしてただ一つのサイトに上記のコード生成を制限:Boost.Preprocessorを使用して(未テスト)の例。 Davidのソリューションとあなたのコメントから


ビル:

template<int... Indices> 
struct indices { 
    typedef indices<Indices..., sizeof...(Indices)> next; 
}; 

template<int N> 
struct build_indices { 
    typedef typename build_indices<N - 1>::type::next type; 
}; 

template<> 
struct build_indices<0> { 
    typedef indices<> type; 
}; 

template<int... Indices> 
void 
bar(int i, indices<Indices...>) 
{ 
    static void (*lookup[])() = { &A<Indices>::f... }; 
    lookup[i](); 
} 

が、その後 barを呼び出す: Nはあなたの定数時間一定になる bar(i, typename build_indices<N>::type())sizeof...(something)

template<int N> 
void 
bar(int i) 
{ bar(i, typename build_indices<N>::type()); } 

bar<N>(i)と呼ばれている:あなたは、その呼び出しの「醜さ」を隠すためにレイヤーを追加することができます。

+0

(コンパイル時に)いくつのケースがあるのか​​分かっていれば、私のためにコンパイラを展開することはできますか? – Predrag

+0

申し訳ありませんが、痛みを感じていますが、ケースの数についての私の知識は 'size ...()'演算子に由来します。私はプリプロセッサがそのような場合に助けにならないことを恐れています。あなたは、その制約を私に助けてくれる何かを考えることができますか? – Predrag

+0

@Predrag十分な上限を選び、あなたが快適であることを確認します。 –

1


テンプレートは、時間の多型は、時間の多型を実行しないでコンパイル実装NO。

4

いいえ、テンプレートはコンパイル時の機能であり、iはコンパイル時に認識されないため、これは不可能です。 A<I>::foo()は、A::foo(i)のようなものに適合させるべきです。

+0

私はそれを認識しています。おそらくいくつかの回避策がある場合は、私はさまよいます。 – Predrag

+1

回避策は、コンパイル時に 'i'を(@ iammilindの解法のように)するか、' A :: foo'にコンパイル時引数を必要としないようにする(私の解)。 – tenfour

+4

3つ目の方法があります(選択肢が限られている場合)。コンパイル時にすべての関数をインスタンス化してルックアップテーブルを作成し、実行時にそれらのいずれかにディスパッチします。私は以前それを使用していましたが、それは痛いですが、実行可能です。 –

1

テンプレート引数はコンパイル時に認識されている必要があります。したがって、コンパイラがA<i>::foo()を渡す方法はありません。あなたは回避したい場合は

あなたもtemplatebar()を行う必要があります。そのために

template<int i> 
void bar() { 
    A<i>::f(); // ok 
} 

、コンパイル時にbar()に引数を知っている必要があります。

8

正確に何をしたいか(つまり、使用するインスタントリミットが少数ですか)に応じて、ルックアップテーブルを作成してそれを動的に使用することができます。完全手動のアプローチのために、オプション0、1、2、および3で、あなたができる:もちろん

void bar(int i) { 
    static void (*lookup[])(void) = { &A<0>::foo, &A<1>::foo, &A<2>::foo, &A<3>::foo }; 
    lookup[i](); 
} 

を、私は、例えば、最も単純なオプションを選択しました。必要な番号が連続していない場合やゼロベースの場合は、配列ではなくstd::map<int, void (*)(void) >を使用することをお勧めします。使用する異なる選択肢の数が多い場合は、手動でテンプレートを入力するのではなく、自動的にテンプレートを強化するコードを追加することもできます。ただし、テンプレートの各インスタンス化によって新しいテンプレートが作成されることを考慮する必要があります。実際にそれが必要かどうかを確認することができます。

EDIT:私はpostと同じ初期化を実装していますが、これはC++ 03の機能のみを使用しているため、答えが長すぎるようです。

Luc Dantonは、とりわけC++ 0x構造体を使ったルックアップテーブルの初期化を含む面白い答えhereを書きました。私はその解決策から、余分な引数を必要とするインタフェースを変更するのはあまり好きではありませんが、中間のディスパッチャで簡単に解決できます。

+0

これは私が探していたものです。配列やマップを自動的に初期化する(あるいはどこかで私に指示する)方法を教えてくれますか? – Predrag

+0

@Predrag:いくつの異なる値が必要ですか?彼らは連続していますか?ゼロベース?また、各インスタンシエーションが新しい関数を作成することに注意してください。これは、バイナリを大きくすることを意味します。 –

+0

これらはゼロベースであり、コンパイル時にどれだけ存在するかを知っています。 – Predrag

関連する問題