templates - C++ runtime type switching (avoiding switch) -
i've been c++ years have not found yet solution problem have. know how solve awesome.
what have @ moment is:
// client code: switch(currentenumvalue) { case myenum::kvalue01: processdata<myenum::kvalue01>(data); break; case myenum::kvalue02: processdata<myenum::kvalue02>(data); break; default: log("invalid command"); break; } // declarations enum class myenum {kvalue01, kvalue02}; class myclass { // code template <myenum> void processdata(char*); /* implemented somewhere else */ } template <> void myclass::processdata<myenum::kvalue01>(char* data); /* implemented somewhere else */ myclass <> void myclass::processdata<myenum::kvalue02>(char* data); /* implemented somewhere else */
i remove switch because of many reasons. instead of need like: processdata<runtime-decltype(currentenumvalue)>(data);
i know typeid , not mixing compile time , runtime together... despite this, find solution anyway, preferably excluding macros.
this class makes jump table given enum
count
size based off constructing template , invoking supplied args. assumes enum values start @ 0, , go count-1.
template<class enum, enum count, template<enum>class z> struct magic_switch { // return value of call magic_switch(args...) template<class...args> using r = std::result_of_t<z<enum(0)>(args...)>; // function pointer jump table: template<class...args> using f = r<args...>(*)(args&&...); // produces single function pointer index , args args... template<size_t i, class...args> f<args...> f() const { using ret = r<args...>; return +[](args&&...args)->ret{ using invoke=z<enum(i)>; return invoke{}(std::forward<args>(args)...); }; } // builds jump table: template<class...args, size_t...is> std::array<f<args...>,size_t(count)> table( std::index_sequence<is...> ) const { return {{ f<is, args...>()... }}; } template<class...args> r<args...> operator()(enum n, args&&...args) { // static jump table case of args...: static auto jump=table<args...>(std::make_index_sequence<size_t(count)>{}); // nth entry in jump table, , invoke it: return jump[size_t(n)](std::forward<args>(args)...); } };
then if have enum:
enum class abc_enum { a, b, c, count };
and function object template:
template<abc_enum e> struct stuff { void operator()() const { std::cout << (int)e << '\n'; } };
you can dispatch:
magic_switch<abc_enum, abc_enum::count, stuff>{}(abc_enum::b);
in case, within template stuff
, enum value compile time constant. call run time constant.
overhead should similar switch statement, or vtable call, depending on compiler optimization wise.
note setting enum
std::size_t
valid.
in c++11 need make_index_sequence
, index_sequence
:
template<size_t...> struct index_sequence {}; namespace details { template<size_t count, size_t...szs> struct sequence_maker : sequence_maker<count-1, count-1, szs...> {}; template<size_t...szs> struct sequence_maker<0,szs...> { using type = index_sequence<szs...>; }; } template<size_t count> using make_index_sequence=typename details::sequence_maker<count>::type; template<class...ts> using index_sequence_for=make_index_sequence<sizeof...(ts)>;
and alias:
template<class sig> using result_of_t=typename std::result_of<sig>::type;
then strip std::
off use in above code.
Comments
Post a Comment