templates metaprogramming switch-statement c++11 variadic

templates - C++/C++ 11-¿Cambiar la instrucción para las plantillas variadic?



metaprogramming switch-statement (2)

Digamos que tengo algunas estructuras como esta:

struct MyStruct1 { inline void DoSomething() { cout << "I''m number one!" << endl; } }; struct MyStruct2 { static int DoSomething() { cout << "I''m the runner up." << endl; return 1; } }; struct MyStruct3 { void (*DoSomething)(); MyStruct3() { DoSomething = &InternalFunction; } static void InternalFunction() { cout << "I''m the tricky loser." << endl; } };

Como puede ver, para las tres estructuras, puedo llamar a DoSomething () en un objeto de esa estructura y hacer que funcione (aunque esto se logra de manera diferente para cada estructura):

MyStruct1 a; MyStruct2 b; MyStruct3 c; a.DoSomething(); // works, calls Struct1''s instance function b.DoSomething(); // works, calls Struct2''s static function, discards return value c.DoSomething(); // works, calls Struct3''s function pointer

Ahora, digamos que puse una selección arbitraria de estas estructuras en una tupla:

tuple<MyStruct2, MyStruct3, MyStruct2, MyStruct1> collection;

Digamos también que quiero tomar uno de esos elementos y ejecutar su función DoSomething() basada en un índice que se determina en el tiempo de ejecución. Para lograr esto, podría usar una instrucción de cambio:

switch(index) { case 0: get<0>(collection).DoSomething(); break; case 1: get<1>(collection).DoSomething(); break; case 2: get<2>(collection).DoSomething(); break; case 3: get<3>(collection).DoSomething(); break; }

Esto funciona bien y es elegante, pero se vuelve muy tedioso, repetitivo y propenso a errores cuando debe hacerse con múltiples tuplas dispuestas de manera diferente (y potencialmente mucho más largas que 4 elementos). Sería muy útil si una instrucción de cambio se generara automáticamente en función del número de elementos en una plantilla variada. Pseudocódigo

template <typename... T> void DoSomethingByIndex(int index, tuple<T...>& collection) { switch(index) { STATIC_REPEAT(sizeof...(T), X) { case X: get<X>(collection).DoSomething(); break; } } }

¿Hay algún mecanismo en C ++ 11 que me permita lograr esto? Si no es así, sé que sin duda puedo juntar una solución con una lista de punteros de función dentro de una plantilla, pero tengo curiosidad si existe algo como esto, ya que sería más adecuado para mis propósitos. Estoy seguro de que la lista de salto generada por compilador de una declaración de cambio sería más eficiente que mi solución de puntero de función hecha en casa también.


Hmmm, estoy tentado de intentar algo como esto:

template<int N, typename ...Args> struct call_N_helper { static void call(const std::tuple<Args...> & t, int i) { if (i == N) std::get<N>(t).call(); else call_N_helper<N-1, Args...>(t, i); } }; template<typename ...Args> struct call_N_helper<0, Args...> { static void call(const std::tuple<Args...> & t, int i) { if (i == 0) std::get<0>(t).call(); } }; template<typename ...Args> void call_N(const std::tuple<Args...> & t, int i) { call_N_helper<sizeof...(Args), Args...>::call(t, i); }

Esto es solo una idea, no probado y todo.


Puede usar una matriz para unir el tiempo de compilación y el tiempo de ejecución: (ab) use variadic templates para inicializar estáticamente los elementos de la matriz, y luego indexarlos en la matriz con el parámetro de tiempo de ejecución. La parte difícil es encontrar el tipo de elemento correcto para la matriz. Además, dado que necesitamos que la plantilla sea variable en los índices de la tupla en lugar de en los elementos de la tupla, usaré mi truco habitual.

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; }; // No need to be variadic on the tuple elements as we don''t care about them // So I''m using perfect forwarding for the tuple template<typename Tuple, int... Indices> void do_something_by_index(Tuple&& tuple, int index, indices<Indices...>) { using std::get; typedef void (*element_type)(Tuple&&); static constexpr element_type table[] = { [](Tuple&& tuple) { get<Indices>(std::forward<Tuple>(tuple)).DoSomething(); } ... }; table[index](std::forward<Tuple>(tuple)); } // Proverbial layer of indirection to get the indices template<typename Tuple> void do_something_by_index(Tuple&& tuple, int index) { typedef typename std::decay<Tuple>::type decay_type; constexpr auto tuple_size = std::tuple_size<decay_type>::value; typedef typename build_indices<tuple_size>::type indices_type; do_something_by_index(std::forward<Tuple>(tuple), index, indices_type{}); }