2016-04-07 1 views
3

様々な小さなタイプを表す型識別器enumを考える:C++では、実行時型のディスクリミネータをテンプレートインスタンスにマップするにはどうすればよいですか(手動ですべてを列挙する必要はありません)。

enum TypesEnum { 
    IntT, 
    DoubleT, 
    ShortStringT 
}; 

は、私がtemplate SomeType<typename A, typename B, typename C>があるとします。これは、レイアウト/ストライドが型によって決まるメモリマップファイルのセットを読み書きする型です。実行時の型は上記のenum discriminatorsの三つ組として格納されます。

SomeType<A,B,C> => SomeType<B,A,C>などのように、これらのファイルをロードして操作できるさまざまなツールを記述する必要があります。したがって、これらのツールでは、ディスク上の型識別器を正しいタイプのテンプレートインスタンスで実装された汎用のラムダラップ処理に変換するかなり面倒なレイヤーがあります。

これは次のようになります。template<A,B,C> class SomeOperationtypedef std::function<bool(void)> some_op_fn_t、実装は、ディスクへの副作用との仕事の束を行う

static std::map< std::tuple<Discrim, Discrim, Discrim>, some_op_fn_t > = 
    { 
     {std::make_tuple(IntT, DoubleT, ShortStringT), SomeOperation<int,double,char[16]>() }, 
     std::make_tuple(IntT, IntT, ShortStringT), SomeOperation<int,int,char[16]>() }, 
     ... 
    }; 
... look up the correct function pointer and call it with the path to the files ... 

これで、タイプのリストとさまざまな操作の数が増えるにつれて、これはすぐに面倒になります。トリックは私です抽象的な/仮想値の型で動作するタイプ消去SomeTypeを単に持っている仮想継承を使用することはできません。インダイレクションとポインター追跡を行うのは非常に遅すぎます。 BLASに直接渡すのに適した、デッド・レコニングと連続した、パックされたバリュー・データ(フロートとダブルの場合)に対応する必要があります。

このようなインターフェース/レイヤーの作成を自動化する技術はありますか?タイプレベルの組み合わせのようなものが役に立ちます。ここでは、列挙型を型に一度だけ接続し、マッピングのすべてのインスタンスを展開できます。それは可能ですか?

私はコードを生成するスクリプトを書くことができます最悪の場合、しかし、うわ...

+1

簡単な部分:あなたは 'int'、' DoubleT'にdouble' 'に' 'enum' IntT'をマッピングする特性を持っている(および/または可能逆に)。より複雑な部分は、enumのデカルト積です。 – Jarod42

+0

私はIntTからint型への特性マッピングを見ることができますが、コンパイル時にのみ可能ですか?製品の列挙型タプルのベクトルがあったとしても、それをループして 'map.insert({i、SomeOperation ()>。type、Trait ()>型を追加することはできませんでした。 ()>。type>()}) '正しい? – experquisite

+0

なぜ、 'std :: function'はvtableの検索よりもずっと高速になると思いますか? vtables(あなたが知っている限り)がヒープ/フリーストアの割り当てを必要とするのに対し、 'std :: function'はパックドメモリで動作できるのでしょうか?vtableは極端な場合に特有のレイアウトを持つ関数のテーブルへのポインタに過ぎません。第二に、あなたのソリューションについて何が面倒なのですか? "これは面倒なこと"は "これはうまくいかない"のようなものです。それらの構造体を手作業で入力しているという問題はありますか? – Yakk

答えて

2

まず、簡単な部分、タイプと列挙型の値との間のマッピング:

template <typename T> struct EnumValue; 

template <> struct EnumValue<int> : std::integral_constant<TypesEnum, IntT> {}; 
template <> struct EnumValue<double> : std::integral_constant<TypesEnum, DoubleT> {}; 
template <> struct EnumValue<char[16]> : std::integral_constant<TypesEnum, ShortStringT> {}; 

次に簡単ヘルパー機能:今

using TupleT = std::tuple<int, double, char[16]>; 

template <typename ... Ts> // Might be T1, T2, T3, but lazy to type 
constexpr auto make_my_pair() 
{ 
    return std::make_pair(std::make_tuple(EnumValue<Ts>::value...), &SomeOperation<Ts...>); 
} 

使用して直積index_sequence

template <std::size_t I> 
constexpr std::pair<TypesEnumTuple, some_op_fn_t> 
make_my_pair() 
{ 
    constexpr std::size_t N = std::tuple_size<TupleT>(); 
    return make_my_pair< 
     std::tuple_element_t<(I/(N * N)) % N, TupleT>, 
     std::tuple_element_t<(I/N) % N, TupleT>, 
     std::tuple_element_t<(I/1) % N, TupleT> 
    >(); 
} 

template <std::size_t ... Is> 
std::map<TypesEnumTuple, some_op_fn_t> 
make_my_map(std::index_sequence<Is...>) 
{ 
    return {make_my_pair<Is>()...}; 
} 

そして最後に:

// 27 = std::tuple_size<TupleT>() * std::tuple_size<TupleT>() * std::tuple_size<TupleT>() 
// as we have T1, T2, T3 
static const std::map<TypesEnumTuple, some_op_fn_t> m = 
    make_my_map(std::make_index_sequence<27>()); 

Demo

+0

素晴らしいです、ありがとうございます。私は今、 'SomeOperation' /' some_op_fn_t'でこのジェネリックを作る方法を理解しようとします。 – experquisite

+1

'std :: size_t compile_time_pow(std :: size_t a、std :: size_t b)'は '27'よりも良いかもしれませんか?私は 'TupleT'を型テンプレートパラメータとし、' std :: integral_constant 'のエイリアスにすることもできますが、それは単にクリーンアップです。 – Yakk

関連する問題