C ++运行时类型切换(避免切换) [英] C++ runtime type switching (avoiding switch)

查看:170
本文介绍了C ++运行时类型切换(避免切换)的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

我已经进入C ++几年了,但我还没有找到解决问题,我不断有。



我现在所拥有的是:

  //客户端代码:
switch(currentEnumValue)
{
case MyEnum :: kValue01:
processData< MyEnum :: kValue01&
break;
case MyEnum :: kValue02:
processData< MyEnum :: kValue02>(data);
break;
默认值:
LOG(无效命令);
break;
}

//声明

枚举类MyEnum {kValue01,kValue02};
class MyClass
{
// code
template< MyEnum> void processData(char *); / *在其他地方实现* /
}
模板<> void MyClass :: processData< MyEnum :: kValue01>(char * data); / *在别的地方实现* /
MyClass<> void MyClass :: processData< MyEnum :: kValue02>(char * data); / *在其他地方实现* /

我想删除交换机,原因很多。代替它,我需要像下面这样: processData< runtime-decltype(currentEnumValue)>(data);



解决方案

此类为给定枚举创建一个跳转表直到某个计数基于大小的构造一些模板并使用提供的args调用它。它假定枚举值从0开始,并转到Count-1。

 模板< class Enum,Enum Count,template< Enum> class Z> 
struct magic_switch {
//调用magic_switch(Args ...)的返回值
template< class ... Args>
使用R = std :: result_of_t< Z< Enum(0)>(Args ...)> ;;
//跳转表的函数指针:
template< class ... Args>
使用F = R< Args ...>(*)(Args& ...);
//为索引I和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)...);
};
}
//构建一个跳转表:
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 ...>对于这种情况下的一个静态跳转表...:
static auto jump = table< Args ...() >(std :: make_index_sequence< size_t(Count)> {});
//查找跳转表中的第n个条目,并调用它:
return jump [size_t(n)](std :: forward< Args>(args)...)
}
};

那么如果你有一个枚举:

 枚举类abc_enum {a,b,c,count}; 

和函数对象模板:

 模板< abc_enum e> 
struct stuff {
void operator()()const {
std :: cout< (int)e < '\\\
';
}
};

您可以派发:

  magic_switch< abc_enum,abc_enum :: count,stuff> {}(abc_enum :: b); 

在任何情况下,在模板 stuff ,您可以将枚举值作为编译时常量。



开销应该类似于switch语句或vtable调用,具体取决于编译器执行的优化方式。



即时示例



请注意,将 Enum 设置为 std :: size_t 是有效的。 / p>

在C ++ 11中,您需要 make_index_sequence index_sequence

 模板< size_t ...> 
struct index_sequence {};
命名空间详细信息{
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 ::



实例


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.

What I have at the 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 would like to remove the switch because of many reasons. Instead of it I would need something like: processData<runtime-decltype(currentEnumValue)>(data);

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.

解决方案

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

then if you have an enum:

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 << '\n';
  }
};

you can dispatch:

magic_switch<abc_enum, abc_enum::count, stuff>{}(abc_enum::b);

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.

Overhead should be similar to a switch statement, or a vtable call, depending on what the compiler does optimization wise.

live example.

Note that setting Enum to std::size_t is valid.

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

and this alias:

template<class Sig>
using result_of_t=typename std::result_of<Sig>::type;

then strip std:: off their use in the above code.

live example.

这篇关于C ++运行时类型切换(避免切换)的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆