【发布时间】:2014-12-09 05:39:12
【问题描述】:
最近我遇到了一个我自己无法解决的问题。假设我们通过函数从网络接收到一些字节:
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 值连接到类型要比为每个 Message 类型编写 case 部分要简单得多。
我也知道,我可能可以这样做:
class Base {};
class Message1 : Base {}
...
class MessageN : Base {}
vector<pair<Id, Base*>> vec;
但我不知道这是否是好的/有效的方法,因为每次我想将 Base* 转换为 MessageX* 时都应该使用 dynamic_cast。
我尝试用 tuple 和 initializer_list 做一些包装类,如下所示:
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)中使用,这是可以理解的为什么不能编译。
如果你有什么建议,那就太好了。
【问题讨论】:
-
您正在寻找工厂模式。您不会将值映射到类型,而是将值映射到执行反序列化的函数/对象。映射可以通过 std::map 完成。查看其他序列化库/框架以获取灵感。
标签: c++ templates switch-statement