我刚刚在我正在进行的游戏引擎中遇到了以下方法:
bool addmsg(int type, const char *fmt, ...) {
if (!connected) return false;
static uchar buf[MAXTRANS];
ucharbuf p(buf, sizeof (buf));
putint(p, type);
int numi = 1, numf = 0, nums = 0, mcn = -1;
bool reliable = false;
if (fmt)
{
va_list args;
va_start(args, fmt);
while (*fmt) switch (*fmt++)
{
case 'r': reliable = true; break;
case 'c':
{
gameent *d = va_arg(args, gameent *);
mcn = !d || d == player1 ? -1 : d->clientnum;
break;
}
case 'v':
{
int n = va_arg(args, int);
int *v = va_arg(args, int *);
loopi(n) putint(p, v[i]);
numi += n;
break;
}
case 'i':
{
int n = isdigit(*fmt) ? *fmt++ - '0' : 1;
loopi(n) putint(p, va_arg(args, int));
numi += n;
break;
}
case 'f':
{
int n = isdigit(*fmt) ? *fmt++ - '0' : 1;
loopi(n) putfloat(p, (float)va_arg(args, double));
numf += n;
break;
}
case 's': sendstring(va_arg(args, const char *), p); nums++; break;
}
va_end(args);
}
int num = nums || numf ? 0 : numi, msgsize = server::msgsizelookup(type);
if (msgsize && num != msgsize) { fatal("inconsistent msg size for %d (%d != %d)", type, num, msgsize); }
if (reliable) messagereliable = true;
if (mcn != messagecn)
{
static uchar mbuf[16];
ucharbuf m(mbuf, sizeof (mbuf));
putint(m, N_FROMAI);
putint(m, mcn);
messages.put(mbuf, m.length());
messagecn = mcn;
}
messages.put(buf, p.length());
return true;
}
这很可怕。人们实际上认为通过网络发送消息是个好主意:
addmsg(MY_ACTION, "rii3ii5", 1, 42, 42, 42, e.type, e.attr1, e.attr2, e.attr3, e.attr4, e.attr5)
哦,是的!超级可读。现在,我想要积极并慢慢地重构这个。我正在考虑使用模板动态解释消息中的类型并正确编码。有没有人建议如何开始这个废话?谢谢!
答案 0 :(得分:1)
有Qt方式,有数据流。
class DataStream {
/* has an internal buffer, can use std::string, std::vector<char> or even uchar [] */
DataStream &operator << (int i) {
putint(internal_buffer, i);
return this;
}
/* ... */
/* Multiple serialization */
void pack() {
return;
}
template <typename T, typename ...Params>
void pack(const T &item, Params&&...params) {
(*this) << item;
pack(std::forward<Params>(params)...);
}
const char* buffer() const {
return internal_buffer.data();
}
};
那样,你有:
template<typename ...Params>
bool addmsg(int type, Params &&... params) {
DataStream stream;
stream << type;
...
stream.pack(std::forward<Params>(params)...);
//Get stream buffer and use that in msg
}
这种事情很危险,你必须确保两个平台上的int都是一样的。使用类型为uint32_t而不是int(在调用addmsg函数时)可能更明智,除非您将DataStream
限制为仅一种类型的整数。
您可以扩展DataStream
以支持其他类型,例如自定义结构:
struct MyStruct {
int a,b,c;
float f;
};
DataStream &operator << (DataStream &stream, const MyStruct &s) {
stream << s.a << s.b << s.c << s.f;
return stream;
}
对e
成员而言,e
成员可以直接传递给addmsg
函数,而不是5个参数:
addmsg(MY_ACTION, 1, 42, 42, 42, e);
答案 1 :(得分:1)
您可以使用函数重载。不需要模板。
#include <string>
constexpr size_t MAXTRANS = 128;
enum reliable_t { reliable };
class message
{
public:
void send() { }
message& operator<<(reliable_t)
{
m_reliable = true;
return *this;
}
message& operator<<(int x)
{
// add int to message
return *this;
}
message& operator<<(float x)
{
// add float to message
return *this;
}
message& operator<<(const std::string& x)
{
// add string to message
return *this;
}
private:
unsigned char m_buf[MAXTRANS];
bool m_reliable = false;
};
int main()
{
message msg;
msg << reliable << 42 << 42 << "ultimate question";
msg.send();
return 0;
}
以下是coliru示例的链接。