列挙値に基づいて特定のテンプレート関数を呼び出す

c++ templates
列挙値に基づいて特定のテンプレート関数を呼び出す

列挙値(カ​​テゴリ)に応じて特定のテンプレート関数 `computecost`を呼び出している次のコードを検討してください。 呼び出しの場合、 `computecost`の引数は同じです。 列挙値とC ++型の間には1対1の対応があります。 `computecost`の引数はすべての呼び出しで常に同じなので、次のコードをよりコンパクトに書くことは可能ですか? タイプ/列挙値ごとに繰り返すことはありません。

mxClassID category = mxGetClassID(prhs);
    switch (category)  {
     case mxINT8_CLASS:   computecost(T,offT,Offset,CostMatrix);   break;
     case mxUINT8_CLASS:  computecost(T,offT,Offset,CostMatrix);  break;
     case mxINT16_CLASS:  computecost(T,offT,Offset,CostMatrix);  break;
     case mxUINT16_CLASS: computecost(T,offT,Offset,CostMatrix); break;
     case mxINT32_CLASS:  computecost(T,offT,Offset,CostMatrix);  break;
     case mxSINGLE_CLASS: computecost(T,offT,Offset,CostMatrix); break;
     case mxDOUBLE_CLASS: computecost(T,offT,Offset,CostMatrix); break;
     default: break;
    }

  5  1


ベストアンサー

`category`を受け取り、適切な関数ポインタを返す関数を作成できます。次に、適切な引数で呼び出されます:

decltype(&computecost) cost_computer(mxClassID const category) {
    switch (category) {
        case mxINT8_CLASS: return &computecost;
        ...
    }
}

cost_computer(mxGetClassID(prhs))(T, offT, Offset, CostMatrix);

または、Markが提案したように、「マップ」を使用します。

std::map)> compute_functions =
    boost::assign::map_list_of
        (mxINT8_CLASS, &computecost)
        // ... and so on
compute_functions[mxGetClassID(prhs)](T, offT, Offset, CostMatrix);

4


まず、これはすべて私にとってちょっと変な匂いがします(タイプコードはいつも私を少し怖がらせます)…​ これは、prhsオブジェクト内の何らかの仮想関数である必要があります。

次に、コードは次のようになります

prhs->computecost(T, offT, Offset, CostMatrix );

「computecost」をメンバー仮想関数にシフトできない場合、コードのどこかにswitchいスイッチ構造が残ってしまいます…​ ただし、同じことを繰り返し実行している場合、および/またはコードのそのセクションを乱雑にしている場合は、ヘルパー関数に巻き上げてください

void computecost( mxClassID category, /* all the other args go here */ )
{
  switch (category)  {
   case mxINT8_CLASS:   computecost(T,offT,Offset,CostMatrix);   break;
   case mxUINT8_CLASS:  computecost(T,offT,Offset,CostMatrix);  break;
   case mxINT16_CLASS:  computecost(T,offT,Offset,CostMatrix);  break;
   case mxUINT16_CLASS: computecost(T,offT,Offset,CostMatrix); break;
   case mxINT32_CLASS:  computecost(T,offT,Offset,CostMatrix);  break;
   case mxSINGLE_CLASS: computecost(T,offT,Offset,CostMatrix); break;
   case mxDOUBLE_CLASS: computecost(T,offT,Offset,CostMatrix); break;
   default: break;
   }
}

次に、コードは次のようになります。

mxClassID category = mxGetClassID(prhs);
computecost(category, T,offT,Offset,CostMatrix );

2


テンプレート化された関数はそれぞれコンパイラーによって異なる関数として扱われるため、それぞれに対して異なる呼び出しを行うことを避ける方法はありません。 各関数には同じシグネチャがあるため、関数ポインターのテーブルを作成することで簡素化できる場合があります。

1


「computecost」関数に動的ディスパッチを使用しない理由はありますか?

最も簡単なことは、継承階層を作成し、動的ディスパッチを使用することです。 クラスIDとして「mxINT8_CLASS」を返す階層内の各タイプは、「computecost」を「computecost」への呼び出しとして実装し、他のすべての組み合わせに対しても同様に実装します。

動的ディスパッチを使用しない強力な理由がある場合は、さまざまな方法で独自の動的ディスパッチを実装することを検討してください。 最も明白で、シンプルで、おそらく保守が容易なのは、すでに持っているものです。 マクロを使用すると、少し複雑になります。または、テンプレートバージョンを試してみてください。

マクロソリューション(次に複雑なもの)では、マクロを使用してリレーションシップを定義し、別のマクロソリューションを使用して各「ケース」を定義し、それらを結合します。

#define FORALL_IDS( macro ) \
   macro( mxINT8_CLASS, signed char ); \
   macro( mxUINT8_CLASS, unsigned char ); \
// ...

#define CASE_DISPATCH_COMPUTECOST( value, type ) \
   case value: computecost( T, offT, Offset, CostMatrix ); break

組み合わせる:

switch ( category ) {
   FORALL_IDS( CASE_DISPATCH_COMPUTECOST );
};

私はこれを過去に見ましたが、好きではありませんが、カテゴリからタイプにマップする必要がある場所が十分にある場合は、簡単に書くのが難しいソリューションを維持することができます。 また、 `FORALL_IDS`マクロを使用して、列挙型から型へ、またはその逆にマッピングするメタプログラミング特性を実装できることに注意してください。

template
struct type_from_id;
#define TYPE_FROM_ID( id, T ) \
   template <> struct type_from_id { typedef T type; }
FORALL_IDS( TYPE_FROM_ID );
#undef TYPE_FROM_ID

template
struct id_from_type;
#define ID_FROM_TYPE( id, T ) \
   template <> struct id_from_type { static const classId value = id; }
FORALL_IDS( ID_FROM_TYPE );
#undef ID_FROM_TYPE

これには多くの欠点があることに注意してください:マクロは本質的に安全ではなく、_this_マクロはタイプを定義し、関数のように振る舞わないため、引数に適切な量の括弧を見つけることが難しくなります。テキスト置換のエラーのすべてのショートになりやすい…​ マクロはコンテキストを認識しないため、使用直後にマクロの定義を解除してスコープを最小化することをお勧めします。 上記の特性を実装することは、それを実行する1つの良い方法です。マクロを作成し、それを使用してテンプレート化された非マクロコードを生成し、マクロをundefします。 残りのコードでは、マクロではなくテンプレートを使用して、一方から他方にマッピングできます。

動的ディスパッチを実装する別の方法は、上記の `switch`ステートメントの代わりにルックアップテーブルを使用することです:

typedef T function_t( T1, T2, T3 ); // whatever matches the `computecost` signature
function_t *lookup[ categories ];   // categories is 1+highest value for the category enum

ルックアップテーブルを手動で初期化するか、上記のマクロを使用できます。コードの複雑さはそれほど変わりません。呼び出し側からルックアップテーブルが初期化される場所に移動するだけです。 呼び出し側では、次のようにします。

lookup[ mxGetClassID(prhs) ]( T, offT, Offset, CostMatrix );

「switch」ステートメントの代わりに、だまされないで、コストは削除されず、初期化にプッシュされます(複数の関数をマップする必要がある場合は、関数の構造体を作成できるため、良いかもしれません)ポインタを使用してすべての初期化を一度に実行すると、手動で調整された独自の「vtable」があり、「vptr」の代わりに「classId」フィールドを使用してインデックスを作成します。

これのテンプレート版はおそらく最も面倒です。 私はそれの楽しみのためだけに実装しようとしますが、実際に運用コードでそれを使用しません。 http://definedbehavior.blogspot.com/2011/08/dynamic-dispatching-of-template.html [テンプレートからルックアップテーブルを構築] ^ [1] ^を試すことができます。これは演習としては楽しいですが、おそらくもっと元の問題よりも複雑です。

あるいは、type-list_タイプのアプローチ(A _Modern C ++ Design

1


タイトルとURLをコピーしました