对于客户端和服务器之间交换的UDP包,我想支持两种字符串字段:
cstring
uint8_t
大小字段vstring
要自我记录我们的包的布局,我想使用简单的结构声明:
struct ABC {
vstring a;
cstring b;
}
并在de / serialization函数中调用重载函数get(char*, vstring& v)
和get(char*, cstring&)
,如下所示:
void deserialize(const char* bytes, ABC& msg) {
get(msg.a);
get(msg.b);
}
void serialize(char* bytes, const ABC& msg) {
put(msg.a);
put(msg.b);
}
但是,对于用户vstring
和cstring
,理想情况下应该像普通std::string
一样。
我的第一个想法是简单地使std::string
成为vstring
和cstring
的公共基础,以便在过载解析期间可以区分这两个类,但对用户来说行为相同。但由于不鼓励从std::string
派生,我不确定该怎么做。
答案 0 :(得分:4)
从std::string
派生的危险是析构函数不是虚拟的,所以有人可能会这样做:
std::string* p = new vstring;
delete p;
你有未定义的行为。如果您认为像这样的代码没有机会在您的环境/系统中编写,请将自己敲出来并从std::string
派生。
准则:
如果vstring
和cstring
类仅在非常有限的受控环境中使用 - 最好是由一个或少数开发人员与您进行预期沟通 - 并且监控实际使用情况,或者很明显动态分配和多态性不会被滥用来处理它们,一切都很好。
在另一个极端 - 如果他们在你的接口中有未指定数量的不受控制的代码,那么通知所有未来的客户开发人员这个问题是不切实际的 - 这不是好事,而且是一个场景你不应该从没有虚拟析构函数的类型派生出来。
那就是说,你真的需要对类型中的序列化样式进行编码吗?他们必须编写序列化和反序列化例程,列出要序列化的字段,并且它们可以指定格式(NUL终止与长度前缀或其他任何其他)。
答案 1 :(得分:2)
在意识形态上,序列化的方式不应该影响数据的类型,所以我最好做这样的事情:
void serializeNullTerminated(char* bytes, const std::string& msg);
void deserializeNullTerminated(const char* bytes, std::string& msg);
void serializeWithSize(char* bytes, const std::string& msg);
void deserializeWithSize(const char* bytes, std::string& msg);
或者将附加参数传递给函数:
void serialize(SerializationType st, char* bytes, const std::string& msg);
void deserialize(SerializationType st, const char* bytes, std::string& msg);
或者你可以制作模板:
template<SerializationType st>
void serialize(char* bytes, const std::string& msg);
template<SerializationType st>
void deserialize(const char* bytes, std::string& msg);
关键是,用户不必处理不同类型的字符串,在他们的代码中,他们只需要选择序列化/反序列化的方式。
答案 2 :(得分:1)
您必须将计算与消息分离。因此,在您的程序中,您使用std::string
,当您需要发送/接收字符串时,您传递的消息符合您的协议。
因此,您的库/应用程序/唯一的公共接口应该是这样的:
int send_message(std::string const &s, int mode, ... destination ... etc);
int receive_message(std::string &s, int mode, ... source ... etc);
(可能char *c
的{{1}}(以null结尾)重载。
模式为标记s
或MODE_CSTRING
。
内部发送/接收:
您甚至不需要为MODE_VSTRING
和cstring
创建课程。