我创建了一个函数序列化程序,它接受Data{一个包含4个成员的类int32,int64,float,double(作为输入,并返回所有元素的字节的编码向量,我将进一步将其传递给反序列化函数以获取原始数据。
std::vector<uint8_t> serialize(Data &D)
{
std::vector<uint8_t> seriliazed_data;
std::vector<uint8_t> intwo = encode(D.Int32); // output [32 13 24 0]
std::vector<uint8_t> insf = encode(D.Int64); // output [233 244 55 134 255 23 55]
// float
float ft = D.Float; // float value eg 4.55
float *a; // I will encode them in binary format
char result[sizeof(float)];
memcpy(result, &ft, sizeof(ft));
// double
double dt = D.Double; // double value eg 4.55
double *c; // I will encode them in binary format
char resultdouble[sizeof(double)];
memcpy(resultdouble, &dt, sizeof(dt));
/////
///// How to bind everything here
/////
return seriliazed_data;
}
Data deserialize(std::vector<uint8_t> &Bytes) /// Vector returned from above function {
Data D2;
D2.Int64 = decode(Bytes, D2);
// D2.Int32 = decode(Bytes, D2);
// D2.float = decode(Bytes, D2);
// D2.double = decode(Bytes, D2);
/// Return original data ( All class members)
return D2;
}
我不知道如何前进。。Q1.如果我把所有东西都绑定在一个向量中,那么在反序列化的同时,我将如何解析它们。应该有某种分隔符吗?Q2.有更好的方法吗?
如果我把所有东西都绑定在一个向量中,我将如何在反序列化时解析它们。应该有某种分隔符吗?
在流中,你要么知道下一个是什么类型,要么你必须在流中有某种类型的指示符"这里给出了int
的vector
与size
"etc:
vector int size elem1 elem2 ... elemX
根据需要支持的类型数量,类型信息可以是1个或多个字节。如果最小的";未知";实体是您的类,那么您需要为每个要支持的类指定一个指示符。
如果您确切地知道流中应该包含什么,则可以省略vector
和int
的类型信息:
size elem1 elem2 ... elemX
Q2.有更好的方法吗?
一个简化可以使serialize
更通用,这样你就可以重用它
std::vector<uint8_t> encode(conts T& x)
对于您想要支持的基本类型(也许还有容器类型(的重载,您可以将其做成这样:
template <class... Ts>
std::vector<uint8_t> serialize(Ts&&... ts) {
std::vector<uint8_t> serialized_data;
[](auto& data, auto&&... vs) {
(data.insert(data.end(), vs.begin(), vs.end()), ...);
}(serialized_data, encode(ts)...);
return serialized_data;
}
然后,只需使用所有成员变量调用serialize
,就可以为类编写序列化,并且可以使复合类型的序列化变得非常容易:
struct Foo {
int32_t x; // encode(int32_t) needed
std::string y; // encode(const string&) needed
std::vector<std::string> z; // encode(const vector<T>&) + encode(const string&)
};
std::vector<uint8_t> encode(const Foo& f) {
return serialize(f.x, f.y, f.z);
}
struct Bar {
Foo f; // encode(const Foo&) needed
std::string s; // encode(const string&) needed
};
std::vector<uint8_t> encode(const Bar& b) {
return serialize(b.f, b.s);
}
以上内容使得类的编码非常直接。要添加序列化,您可以添加一个适配器,该适配器只需引用要序列化的对象,对其进行编码,并将编码的数据写入ostream
:
struct BarSerializer {
Bar& b;
friend std::ostream& operator<<(std::ostream& os, const BarSerializer& bs) {
auto s = encode(bs.b); // encode(const Bar&) needed
return os.write(reinterpret_cast<const char*>(s.data()), s.size());
}
};
您可以用类似的方式制作deserialize
函数模板和decode
重载。
有一个高吞吐量的解决方案,但它的工作原理需要一些注意事项。
- 您有一个支持打包对齐的编译器和体系结构。GCC、clang、ICC和MSVC都可以,但效率取决于您的体系结构。可能是个好消息:i386/x86_64在不为未对齐的内存访问支付惩罚方面堪称传奇。SIMD不起作用
- 您必须在结构中使用POD成员-std::vector、std::string、映射、集合、deques、列表、智能指针在这里不起作用。但是一束int和float就可以了。可以通过对其他结构的自定义重新实现来解决这个问题,但让我们保持简单。您可以嵌入其他
structs
等等,只要它们也是POD即可。(POD==普通旧数据https://en.cppreference.com/w/cpp/language/classes#POD_class) - 您的数据以发送方和接收方相同的endianness传输(也适用于实现例如
operator int32_t()
,但#define
或consteval
到endianness的自定义数据类型( - 您的通信通道正在重复发送单个结构,或者可以依靠一个公共标头(用于多个结构类型(在
switch
中进行调度
然后您的代码变为:
#pragma pack(push,1)
struct D
{
int32_t Int32;
int64_t Int64;
float Float;
double Double;
};
#pragma pack(pop)
const char * serialize(const Data& d)
{
return reinterpret_cast<const char *>(&d);
}
const Data& deserialize(const char * buffer)
{
return *reinterpret_cast<const Data*>(buffer);
}
您需要多少数据?始终为sizeof(D)
。因此,serialize
将始终为sizeof(D)
数据提供一个const char *
指针,并且您需要读取sizeof(D)
数据才能传递给deserialize
。
现在,当然,您可以在std::vector<uint8_t>
中弹出所有这些。但这里的巧妙之处在于,根本不需要内存副本。您可以直接使用对象本身进行序列化,以及来自任何反序列化介质的原始char *
数据,而无需任何副本或昂贵的逐字段操作。
哦。编辑后补充道:谷歌原型软件或Cap‘n原型软件可能会对你想要解决的问题有所帮助。