最近我遇到了一个我无法独自解决的问题。假设我们通过函数从网络接收一些字节:
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/