2016-04-07 3 views
3

ein enum vom Typ Scheidern, die verschiedene kleine Arten Gegeben:Wie in C++, um Laufzeittyp-Diskriminatoren Template-Instanzen zuzuordnen (ohne sie manuell alle aufzuzählen)?

enum TypesEnum { 
    IntT, 
    DoubleT, 
    ShortStringT 
}; 

Angenommen, ich habe eine template SomeType<typename A, typename B, typename C>. Dies ist ein Typ, den ich in Sets von Memory-Mapped-Dateien lese und schreibe, deren Layout/Schritt von den Typen bestimmt wird; Die Laufzeittypen werden als Triplet der obigen Enum-Diskriminatoren gespeichert.

Ich muss verschiedene Tools schreiben, die diese Dateien laden und Operationen auf ihnen ausführen können, wie SomeType<A,B,C> => SomeType<B,A,C> und so weiter. In diesen Werkzeugen habe ich daher eine ziemlich unbeholfene Schicht, die die Typdiskriminatoren auf der Platte in generische lambdas-Umhüllungsoperationen übersetzt, die mit Schabloneninstanzen für die richtigen Typen implementiert sind.

Das sieht aus wie:

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 ... 

wo typedef std::function<bool(void)> some_op_fn_t, deren Implementierungen in template<A,B,C> class SomeOperation mit Nebenwirkungen auf der Festplatte eine Reihe von Arbeit zu tun.

Nun wird dies schnell ziemlich langweilig, wenn die Liste der Typen und Anzahl der verschiedenen Operationen wächst. Der Trick ist, ich kann nicht verwenden virtuelle Vererbung, um einfach eine Art gelöscht SomeType, die auf abstrakte/virtuelle Werttypen funktioniert; es ist viel zu langsam, um die Indirektion und den Zeiger zu verfolgen. Ich brauche Koppelnavigation und zusammenhängende, gepackte Wertdaten, die (bei Floats und Doubles) geeignet sind, direkt an BLAS weitergeleitet zu werden.

Gibt es Techniken zur Automatisierung der Erstellung von Schnittstellen/Ebenen wie dieser? Etwas wie eine Typ-Level-Kombination würde helfen, wo ich die Enumeration nur einmal mit den Typen verbinden und dann alle Instanzen des Mappings erweitern könnte. Ist das möglich?

Im schlimmsten Fall kann ich ein Skript schreiben, um den Code zu generieren, aber igitt ...

+1

Der einfache Teil: Sie können eine Züge haben zur Karte '' enum' IntT' zu 'int',' DoubleT' zu 'double' (und/oder und umgekehrt). Der schwierigere Teil ist das kartesische Produkt Ihres Enums. – Jarod42

+0

Hmm Ich kann eine Merkmalszuordnung von 'IntT' zu' int' sehen, aber nur zur Kompilierzeit? Selbst wenn ich einen Vektor von Produkt-Enum-Tupeln hätte, könnte ich immer noch nicht darüber hinwegschleifen, um 'map.insert ({i, SomeOperation ()>. Type, Merkmal ()>. Type, Merkmal hinzuzufügen()>. type>()}) richtig? – experquisite

+0

Also, warum denkst du, dass 'std :: function' viel schneller sein wird als eine vtable-Suche? Kann die 'std :: -Funktion' auf gepackten Speicher angewendet werden, während Vtables (soweit Sie wissen) Heap-/Free-Store-Zuweisung erfordern?Eine Vtable ist nur ein Zeiger auf eine Tabelle von Funktionen mit einem speziellen Layout im Extremfall. Zweitens, * was * über Ihre Lösung wird langweilig? "Das wird langweilig" ist wie "das geht nicht"; ist das Problem, dass Sie diese Strukturen von Hand eingeben? – Yakk

Antwort

2

Zuerst einen einfachen Teil, eine Zuordnung zwischen Typ und Enum-Wert:

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> {}; 

Dann einen einfachen Helfer Funktion:

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...>); 
} 

Nun ist die cartesianischen Produkt mit 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>()...}; 
} 

Und schließlich:

// 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

Super, danke. Ich werde nun versuchen, herauszufinden, wie man das generische in 'SomeOperation' /' some_op_fn_t' macht. – experquisite

+1

'std :: size_t compile_time_pow (std :: size_t a, std :: size_t b)' könnte besser sein als '27'? Ich würde 'TupleT' auch einen Typ-Template-Parameter und vielleicht einen Alias ​​für' std :: integral_constant 'machen, aber das ist nur Cleanup. – Yakk