如何将数据成员的所有字节数组都打包到一个向量中



我创建了一个函数序列化程序,它接受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.有更好的方法吗?

如果我把所有东西都绑定在一个向量中,我将如何在反序列化时解析它们。应该有某种分隔符吗?

在流中,你要么知道下一个是什么类型,要么你必须在流中有某种类型的指示符"这里给出了intvectorsize"etc:

vector int size elem1 elem2 ... elemX

根据需要支持的类型数量,类型信息可以是1个或多个字节。如果最小的";未知";实体是您的类,那么您需要为每个要支持的类指定一个指示符。

如果您确切地知道流中应该包含什么,则可以省略vectorint的类型信息:

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重载。

有一个高吞吐量的解决方案,但它的工作原理需要一些注意事项。

  1. 您有一个支持打包对齐的编译器和体系结构。GCC、clang、ICC和MSVC都可以,但效率取决于您的体系结构。可能是个好消息:i386/x86_64在不为未对齐的内存访问支付惩罚方面堪称传奇。SIMD不起作用
  2. 您必须在结构中使用POD成员-std::vector、std::string、映射、集合、deques、列表、智能指针在这里不起作用。但是一束int和float就可以了。可以通过对其他结构的自定义重新实现来解决这个问题,但让我们保持简单。您可以嵌入其他structs等等,只要它们也是POD即可。(POD==普通旧数据https://en.cppreference.com/w/cpp/language/classes#POD_class)
  3. 您的数据以发送方和接收方相同的endianness传输(也适用于实现例如operator int32_t(),但#defineconsteval到endianness的自定义数据类型(
  4. 您的通信通道正在重复发送单个结构,或者可以依靠一个公共标头(用于多个结构类型(在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原型软件可能会对你想要解决的问题有所帮助。

相关内容

  • 没有找到相关文章

最新更新