C++ 运行时类型切换(避免切换)
我已经使用 C++ 多年了,但我还没有找到解决我经常遇到的问题的方法.知道如何解决它会很棒.
I've been into C++ for some years but I have not found yet the solution to a problem I constantly have. Know how to solve it would be awesome.
我现在拥有的是:
// 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 */
出于多种原因,我想移除开关.而不是它,我需要类似的东西: processData<runtime-decltype(currentEnumValue)>(data);
I would like to remove the switch because of many reasons. Instead of it I would need something like: processData<runtime-decltype(currentEnumValue)>(data);
我知道 typeid 以及不将编译时间和运行时混合在一起......但尽管如此,我还是想找到一些解决方案,最好排除宏.
I know about typeid and about not mixing compile time and runtime together... but despite this, I would like to find some solution anyway, preferably excluding macros.
推荐答案
这个类根据构造为给定的 Enum
创建一个跳转表,直到某个 count
大小一些模板并使用提供的参数调用它.它假定枚举值从 0 开始,然后转到 Count-1.
This class makes a jump table for a given Enum
up to a certain count
size based off constructing some template and invoking it with the supplied args. It assumes the enum values start at 0, and go to Count-1.
template<class Enum, Enum Count, template<Enum>class Z>
struct magic_switch {
// return value of a call to magic_switch(Args...)
template<class...Args>
using R = std::result_of_t<Z<Enum(0)>(Args...)>;
// A function pointer for a jump table:
template<class...Args>
using F = R<Args...>(*)(Args&&...);
// Produces a single function pointer for index I and 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 a 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) {
// a static jump table for this case of Args...:
static auto jump=table<Args...>(std::make_index_sequence<size_t(Count)>{});
// Look up the nth entry in the jump table, and invoke it:
return jump[size_t(n)](std::forward<Args>(args)...);
}
};
如果你有一个枚举:
enum class abc_enum { a, b, c, count };
还有一个函数对象模板:
and a function object template:
template<abc_enum e>
struct stuff {
void operator()() const {
std::cout << (int)e << '
';
}
};
您可以派送:
magic_switch<abc_enum, abc_enum::count, stuff>{}(abc_enum::b);
在任何情况下,在模板 stuff
中,您都可以将枚举值作为编译时间常数.你用运行时常量调用它.
in any case, within the template stuff
, you get the enum value as a compile time constant. You call it with a run time constant.
开销应该类似于 switch 语句或 vtable 调用,具体取决于编译器如何优化.
Overhead should be similar to a switch statement, or a vtable call, depending on what the compiler does optimization wise.
现场示例.
请注意,将 Enum
设置为 std::size_t
是有效的.
Note that setting Enum
to std::size_t
is valid.
在 C++11 中,您需要 make_index_sequence
和 index_sequence
:
In C++11 you need make_index_sequence
and 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)>;
还有这个别名:
template<class Sig>
using result_of_t=typename std::result_of<Sig>::type;
然后在上面的代码中去掉 std::
的使用.
then strip std::
off their use in the above code.
现场示例.
相关文章