c++ - 将值映射到类型以避免 C++ 中的 switch 语句

标签 c++ templates switch-statement

最近我遇到了一个我无法独自解决的问题。假设我们通过函数从网络接收一些字节:

vector<char> receive (); 

当我获取字节时,我准确地知道什么是前 10 个字节,以及如何解释它们。消息的其余部分与 Id 中使用的值(这 10 个字节)相关联。所以,例如它可以这样表达:

auto bytes = receive ();
Id id = get_id (bytes);

switch (id) { 
    case Id::Message1:
    {
        Message1 message = convert_bytes<Message1> (bytes);
        notify (message);
        break;
    }

    case Id::Message2:
    {
        Message2 message = convert_bytes<Message2> (bytes);
        notify (message);
        break;
    }
    ...

    case Id::MessageN:
    {
        MessageN message = convert_bytes<MessageN> (bytes);
        notify (message);
        break;
    }
}

如您所见,每个案例部分仅在消息 ID 和消息类型方面有所不同。

我的问题是:是否有可能将 Id 值映射到特定类型的 Message 以简化上述代码而不使用 switch 语句?

auto bytes = receive ();
Id id = get_id (bytes);

// Here some magic retrieval of Message type based on Id value and invoking 
// appropriate convert_bytes specialization
// auto message = convert_bytes<get_message_type(id)> (bytes); (Y)

当然 (Y) 是错误的,但也许您知道用其他方式来理解这个概念。

我知道这种将 Id 值连接到类型的逻辑必须在某处实现,但仅将 Id 值连接到类型比为每个消息类型编写 case 部分要简单得多。

我也知道,我可能会做这样的事情:

class Base {};
class Message1 : Base {}
...
class MessageN : Base {}

vector<pair<Id, Base*>> vec;

但我不知道这是否是好的/有效的方法,因为每次我想将 Base* 转换为 MessageX* 时我都应该使用 dynamic_cast。

我试着用元组和初始化列表做一些包装类,像这样:

struct Message1 { int x; };
struct Message2 { double z; };

enum class Id { Message1, Message2 };

template <typename Id_type, typename ... Types>
class Wrapper { 
    public:
        Wrapper (tuple<Types ...> t, const initializer_list<Id_type>& il) : tpl (t), ids (il) {


        }

       MessageType_depended_on_id get_message (Id id, vector<char> bytes); // (X)


    private:
        tuple<Types ...> tpl;
        vector<Id_type> ids;
};

tuple<Message1, Message2> tpl;
Wrapper<Id, Message1, Message2> wrapper (tpl, {Id::Message1, Id::Message2});

(X) 但是没有办法根据 id 值指定成员函数类型,还是我遗漏了什么?

我最后的想法是在模板特化中实现每个案例部分,像这样:

template <Id id> 
class Type_retriever {};

template <>
class Type_retriever<Id::Message1> {
    public:
        static Message1 get_msg (const vector<char>& bytes) { 
            cout << "Message1" << endl;
            return convert_bytes<Message1> (bytes);
        }
};

template <>
class Type_retriever<Id::Message2> {
    public:
        static Message2 get_msg (const vector<char>& bytes) { 
            cout << "Message2" << endl;
            return convert_bytes<Message2> (bytes);
        }
};

template <typename Type>
void notify (Type message) { }

auto bytes = receive ();
auto id = get_id (bytes);
notify (Type_retriever<id>::get_msg (bytes));

但这不会编译,因为‘id’的值在常量表达式 (gcc) 中不可用,这就是为什么无法编译的原因。

如果您有任何建议,那就太好了。

最佳答案

您可以使用注册/插件机制摆脱 switch 语句。

注册函数和使用它们的接口(interface):

typedef void (*MessageDispatcher)(const vector<byte>& bytes);

void registerMessageDispatcher(Id id, MessageDispatcher dispatcher);

void dispatchMessage(Id id, const vector<byte>& bytes);

在实现中:

static std::map<Id, MessageDispatcher> messageDispatcherMap;

void registerMessageDispatcher(Id id, MessageDispatcher dispatcher)
{
   messageDispatcherMap[id] = dispatcher;
}

void dispatchMessage(Id id, const vector<byte>& bytes)
{
   std::map<Id, MessageDispatcher>::iterator iter = messageDispatcherMap.find(id);
   if ( iter == messageDispatcherMap.end() )
   {
      // Deal with the error condition.
      return;
   }

   // Dispatch the message.
   iter->second(bytes);

}

为各种消息类型创建函数。

void dispatchMessage1(const vector<byte>& bytes)
{
   Message1 message = convert_bytes<Message1> (bytes);
   notify (message);
}

void dispatchMessage2(const vector<byte>& bytes)
{
   Message2 message = convert_bytes<Message2> (bytes);
   notify (message);
}

void dispatchMessage3(const vector<byte>& bytes)
{
   Message3 message = convert_bytes<Message3> (bytes);
   notify (message);
}

等...

注册函数。

registerMessageDispatcher(ID::Message1, dispatchMessage1);
registerMessageDispatcher(ID::Message2, dispatchMessage2);
registerMessageDispatcher(ID::Message3, dispatchMessage3);

现在,处理消息的代码将是:

auto bytes = receive ();
Id id = get_id (bytes);
dispatchMessage(id, bytes);

关于c++ - 将值映射到类型以避免 C++ 中的 switch 语句,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/27372176/

相关文章:

c++ - 如何获取数组中字符串元素的字符数? [C++]

c++ - 如何在 C++ 中将整个 xml 文件源存储为字符串

c++ - 寻找一个 C++ GUI 库,您可以在其中设计精美的 GUI(例如游戏),

c++ - 使用指向数据成员的指针作为非类型模板参数

language-agnostic - Switch (Case) 总是出错吗?

c# - 带字符串的 Switch 语句 C#

c++ - 如何将模板中具有非依赖名称的 msvc++ 代码移植到 Linux?

c++ - 表达式模板 - 无法专门化函数模板

php - Woocommerce 不同的产品模板

c - switch 和 if else 语句的区别