聊聊llbc Stream、序列化、反序列化

返回目录
在实际项目开发中,数据的序列化、反序列化会成为一个比较麻烦的问题,业界也有许多解决方案可以选择,包括jsonxmlgoogle-protobuf,...这些方案都各有各的好处及弊端。
json/xml/其它文本格式协议:human readable格式,但性能并不是最好的,同时在序列化后的数据大小在跟google-protobuf这类二进制协议相比起来,也没有任何优势,实际在项目开发过程中,协议序列化后是否human readable并不是非常重要。所以此类协议在实际项目开发过程中并不会优先采用,除非有特殊需求。
google-protobuf/其它二进制格式协议:human unreadable格式,在性能及序列化后的数据大小上,都是比较好的,而且大多数也都提供了数据版本的向后兼容支持。
llbc Stream:llbc提供的数据序列化支持,跟google-protobuf同类协议,为二进制格式协议,但不提供数据向后兼容支持。Stream的优势是不需要向google-protobuf一样,要而外编写一个message定义并导出、生成c++代码文件。Stream允许你直接将你项目中任意复杂的c++ class object或者struct object进行序列化及反序列化。极大的简化序列化/反序列化需要做的工作。

在c/c++开发过程中,一说到序列化/反序列化支持,最过简单的莫过于下面这种:

#pragma pack(1)
struct Data
{
    int intVal;
    double dblVal;
}
#pragma pack()

通过将字节对齐限定到1字节,在需要被序列化的时候,直接memcpy(dst, &data, sizeof(data))完成数据的序列化,充序列化同样。但这样做的问题在于如果struct或者class字义中有vptr或者一些无法memcpy就能够完成序列化的数据成员的时候,要怎么办呢?这个时候,c++的解决方案是定义一个统一的基类ISerializable,并要求所有的需要序列化的类都统一继承此基类,并实现相应的接口。这里有一个问题,在实际开发中,这个约束会让开发者觉得烦,可能我的一个Data对象里面有很多数据成员都是一些简单封装的Structure,并不存在复杂的对象,只有少数几个有,而且就算需要实现相对的序列化/反序列化方式,也最好不要限定我去一定要继承什么基类之类的,只要我有这个方法,你就调用,如果没有你就帮我memcpy就行,这样最舒服。而这些llbc的Stream类可以做到,llbc的Stream在序列化任何一个对象的时候,会通过模板机制完成序列化方法void Serialize(LLBC_Stream &stream)的搜索,如果搜索不到,会看此类是否是std标准模板库的的标准容器(vector, queue, map, set, ...),如果都搜索不到,最后再进行memcpy(dst, &data, sizeof(data))的copy,如果有搜索到,会进行恰当的序列化。反序列化同样也会自动化进行上述步骤的方法搜索及类型确认,这些工作在编译阶段就帮你完成,你要做的事情,只量专心的定义数据结构并思考如何编写的的代码逻辑即可,序列化/反序列化工作交给llbc.Stream完成即可,不会引入任何过多的工作量及性能损耗。示例如下:

struct DataA
{
    int val1;
    int val2;
    int val3;
};

struct DataB
{
    DataA dataA;  // 以memcpy(dst, &data, sizeof(data))方式序列化/反序列化
    std::vector strings; // 以std标准容器方式序列化/反序列化,支持任意复杂嵌套
    std::map keywords; // 以std标准容器方式序列化/反序列化,支持任意复杂嵌套

    void Serialize(LLBC_Stream &stream)
    {
        stream.Write(dataA);
        stream.Write(strings);
        stream.Write(keywords);
    }

    bool DeSerialize(LLBC_Stream &stream)
    {
        stream.Read(dataA);
        stream.Read(strings);
        stream.Read(keywords);
        return true;
    }
};

// 实际序列化DataB时,变得非常简单
DataB dataB; // 定义DataB对象并作初始化
LLBC_Stream stream; // 定义Stream对象
stream.Write(dataB); // 完成对象序列化

// 充序列化同样:
char *serializedDataB; // 数据
size_t dataBLen = xxx; // 数据长度
LLBC_Stream stream(serializedDataB, dataLen); // Attach到Stream
DataB dataB; // 定义DataB对象
stream.Read(dataB); // 完成反序列化

更详细的用法请参考testsuite中的TestCase_Com_Stream.cpp文件。

你可能感兴趣的:(聊聊llbc Stream、序列化、反序列化)