c++序列化和反序列化深入实践

1 序列化与反序列化目的 2

2 序列化库的设计思路 2

2.1 思路 2

2.2 序列化流程图 3

2.3 序列化中的主要结构 5

3 TypeInfo的设计 5

3.1 类的信息 5

3.2 类的信息结构 6

3.3 类型注册 8

3.3.1 宏定义使用示例 8

3.3.2 全局类型信息数组 9

3.3.3 注册类 9

3.3.4 注册类名 9

3.3.5 注册类的父类 10

3.3.6 构造成员变量信息 10

3.3.7 注册类的成员变量 10

3.3.8 构造Function 10

3.3.9 注册成员函数 12

4 Variant的设计 12

4.1 可保存的类型 12

4.2 Variant结构 12

4.3 主要的功能函数 16

5 Serializer的设计 16

5.1 JsonWriter示例 16

6 Deserializer的设计 17

6.1 JsonReader示例 17

1. 序列化与反序列化目的

通常在程序运行过程中我们需要对一个对象当前的状态进行存储或者传输,在传递和保存对象时,保证对象的完整性和可传递性。对象转换为有序字节流,以便在网络上传输或者保存在本地文件中,之后根据字节流中保存的对象状态及描述信息,通过反序列化重建对象,反序列化之后的对象状态跟序列化之前的对象状态一致。

2. 序列化库的设计思路

在实际程序开发中我们可能会写出一个很复杂的类,它有一系列的继承路径(这里只讨论单继承),有很多成员变量,成员变量中不仅有int,float,double 等等内建类型,还有类,指针等。在程序运行过程中,经过各种运算,这些成员变量都有具体的值,现在要将这个类持久化或者通过网络传输。

将一个类持久化,一般是将它转为二进制字符串或者普通文本格式的字符串,都需要一定的规则,反序列化时通过这种规则能将其恢复至当时的状态。

下面以“将一个类CustomClass序列化保存到一个json中,并可以根据这个json反序列化为CustomClass类对象”为例,进行讨论。

2.1 思路

要使CustomClass具有序列化的能力,必须注册它,生成一个TypeId;

设计一个Variant类,它可以保存待序列化类CustomClass的信息,并且可以互相转换,例如:

CustomClass cc;  Variant  v = Variant::FromValue(cc);

CustomClass cc = v.Value<CustomClass>();

每个CustomClass类型都有唯一的TypeId,Variant 中持有这个TypeId,根据TypeId,Variant可以获取其保存的CustomClass的类型信息TypeInfo。

设计Serializer,提供void Serialize(const Variant &val) 方法,通过Variant获取TypeInfo,将TypeInfo的中的属性和对应的值写到json中。

设计Deserializer,提供 Variant Deserialize(const ReadBuffer &buffer)方法,将根据json的内容构造Variant的TypeInfo。

2.2 序列化流程图

将一个类序列化为json字符串

                                                                                  图2.1 序列化流程图

2.3 序列化中的主要结构

                                                                                    图2.2  序列化主要结构

3. TypeInfo的设计

类型信息TypeInfo是整个序列化系统的核心,它记录了一个类型CustomClass的所有信息。

3.1 类的信息

一个类有很多信息我们需要记录。

是否存在继承关系,它继承于哪个类;

所在的命名空间;

成员变量的名称;

成员变量的类型;

成员函数;

3.2 类的信息结构

需要设计一种结构保存一个类的信息。

成员变量信息结构

struct MemberInfo

{

    enum Flag

    {

        NONE = 0,

        READ = 1,

        WRITE = 2,

        READ_WRITE = READ | WRITE

    };

    std::string name;

    std::string desc;

    std::uint32_t classId;

    std::uint32_t typeId;

    Flag flag;

};

成员函数信息结构

struct FunctionInfo

{

    std::string name;

    std::string param;

    std::string protoType;

    std::uint8_t argc;

};

类信息结构

struct TypeInfo

{

    std::uint32_t id;

    std::uint32_t parentId;

    std::string namespaceName;

    std::string name;

    std::string desc;

    std::vector<MemberInfo*> properties;

    std::vector<FunctionInfo*> functions;

};

TypeInfo::id,类的唯一id。

TypeInfo::parent,类的父类唯一id。

TypeInfo::namespaceName,类的命名空间名称。

TypeInfo::name,类名,唯一。

TypeInfo::desc, 类的描述。

TypeInfo::properties 所有注册的成员变量

TypeInfo::functions 所有注册的成员函数

MemberInfo::Flag 是否可读可写

MemberInfo::name 类的成员变量名

MemberInfo::desc 类的成员变量描述

MemberInfo::classId 类的id,即该成员变量所在类的id

MemberInfo::typeId 该成员变量的类型id

        若一个类有父类,如果要获取该类的所有成员变量信息,不仅要获取该类的properties,还需要通过parent获取其父类的TypeInfo,再获取父类TypeInfo中的properties,若父类还有父类,则重复该操作,直到获取到该类的所有成员信息。

3.3 类型注册

我们定义以下几个宏来方便注册自定义类

REGISTER_BEGIN(THIS_TYPE)

REGISTER_END()

REGISTER_PARENT(PARENT)

REGISTER_MEMBER(MEM_NAME, DESC)

REGISTER_FUNCTION(FUNCTION)

3.3.1 宏定义使用示例

class MyClass : public Object

{

public:

    MyClass() = default;

    ~MyClass() = default;

    int GetX() { return x_; }  

    void SetX(int x) { x_ = x; }

    double GetY() { return y_;}

    void SetY(double y) { y_ = y; }

    int Sum(int n, int m) { return n + m; }

    int x_;

    double y_;

};

REGISTER_BEGIN(MyClass)

REGISTER_PARENT(MyClass, Object)

REGISTER_MEMBER(X, "长")

REGISTER_MEMBER(Y, "宽")

REGISTER_FUNCTION(Sum)

REGISTER_END()

3.3.2 全局类型信息数组

std::vector<TypeInfo> buildinTypeNodes

std::vector<TypeInfo> userTypeNodes

设置两个全局数组,一个是内建类型信息数组buildinTypeNodes,一个是自定义类类型信息数组userTypeNodes。程序启动后,开始注册类的信息TypeInfo,将内建类型和类的各种信息保存到对应的全局数组中。

注册一个新类型CustomClass,首先判断全局数组userTypeNodes中是否存在,若存在直接返回,若不存在,则构建好类型信息TypeInfo,然后放到全局数组userTypeNodes中。

3.3.3 注册类

std::uint32_t RegisterType(const std::string &name, std::uint32_t parentId);

name为类名,通过类名从userTypeNodes中获取TypeInfo,若存在则注册父类,RegisterParent(TypeInfo::id, parent)并返回TypeInfo::id,若不存在则构建一个新的TypeInfo,根据userTypeNodes的大小给定一个TypeInfo::id,并将这个TypeInfo保存到userTypeNodes中。

3.3.4 注册类名

对应宏定义REGISTER_BEGIN(THIS_CLASS)。

void  RegisterTypeName(std::uint32_t typeId, const std::string &name);

通过typeId获取TypeInfo,将TypeInfo::name 赋值为name。

3.3.5注册类的父类

对应宏定义REGISTER_PARENT(PARENT)。

void  RegisterParent(std::uint32_t typeId, std::uint32_t parentId);

通过typeId获取TypeInfo,将TypeInfo::parent 赋值为parentId。

3.3.6 构造成员变量信息

template<class  ClassType,  class GetterHolder, class SetterHolder>

MemberInfo* MakeMenberInfo(GetterHolder get, SetterHolder set,

std::uint32_t classId, const std::string &name, const std::string &desc);

GetterHolder,变量的Get方法。

SetterHolder,变量的Set方法。

 classId,变量所属的类的唯一id。

name,变量的名称。

desc,变量描述。

typeId,变量自己的唯一id,通过GetterHolder可以获取变量自己的类型,boost::function_types::result_type<GetterHolder>::type,  通过类型从全局数组中获取TypeInfo,TypeInfo::id就是该变量的typeId。

3.3.7 注册类的成员变量

对应宏定义REGISTER_MEMBER(MEM_NAME, DESC)。

void RegisterMember(std::uint32_t  typeId,  const MemberInfo* property);

通过typeId获取TypeInfo,将property添加到TypeInfo:: properties中。

3.3.8 构造Function

Function类是类的成员函数类型

struct Function

{

    virtual std::uint32_t GetArgCount() = 0;

    virtual std::uint32_t GetRetType() = 0;

    virtual std::uint32_t GetArgType() = 0;

    virtual void Invoke(void *obj, Variant &ret,

    std::stack<Variant> ¶ms) = 0;

std::string name;

    std::uint8_t argc;

};

Function::name, 成员函数名。

Function::argc,成员函数参数个数。

以下是不同参数个数的子类Function的定义。

  • 无参函数

template<class ClassType, class FuncType,

    class RetType>

struct ParamterlessFunction : Function{};

  • 一个参数函数

template<class ClassType, class  FuncType, 

    class RetType, class P1>

struct UnaryFunction : Function{};

  • 两个参数函数

template<class ClassType, class FuncType,

    class RetType, class P1, class P2>

struct BinaryMemFunction : Function{};

  • 三个参数函数

template<class ClassType, class FuncType,

    class RetType, class P1, class P2, class P3>

struct TernaryMemFunction : Function{};

分别实现不同参数个数的Function子类。

每个Function子类必须实现Function中的GetArgCount, GetArgType,Invoke这三个纯虚函数。

3.3.9 注册成员函数

对应宏定义REGISTER_FUNCTION(FUNCTION)。

void RegisterMemFunction(std::uint32_t typeId, const Function* func);

通过typeId获取TypeInfo,将func添加到TypeInfo:: functions中。

4. Variant的设计

Variant类可以保存所有内建类型和所有注册过的自定义类,并且可以互相转换。

4.1 可保存的类型

内建类型

std::uint32_t n = 10;

Variant v = Variant::FromValue(n);

std::uint32_t n = v.Value<std::uint32_t>();

自定义类型

CustomClass cc;

Variant  v = Variant::FromValue(cc);

CustomClass cc = v.Value<CustomClass>();

4.2 Variant结构

struct Variant

{

    struct String

    {

        char* str;

        std::uint32_t size;

    };

    struct Object

    {

        void *obj;

        Allocator *alloc;

    };

    struct Pair

    {

        Variant *key;

        Variant *value;        

    };

    struct Map

    {

        Pair *elems;

        std::uint32_t size;

        std::uint32_t capacity; 

    };

    struct Array

    {

        Variant **elems;

        std::uint32_t size;

        std::uint32_t capacity;

    };

    union Number

    {

        struct 

        {

            std::int8_t i8;

        } i8;

        struct 

        {

            std::uint8_t u8;

        } u8;

        struct 

        {

            std::int16_t i16;

        } i16;

        struct 

        {

            std::uint16_t u16;

        } u16;

        struct 

        {

            std::int32_t i32;

        } i32;

        struct 

        {

            std::uint32_t u32;

        } u32;

        struct 

        {

            float f;

        }f;

        double d;

        std::int64_t i64;

        std::uint64_t u64;

    };

    

    struct Data

    {

        union Value

        {

            void *ptr;

            Number num;

        } value;

        std::uint32_t typeId;

    } data;

};

Variant::data::typeId是它存储的类型的唯一id。

Variant::data::value::num保存的是内建类型的值。

Variant::data::value::ptr 保存自定义类型指针

4.3 主要的功能函数

FromValue

template <typename T> Variant FromValue(T t);

将一个原始类型T转为Variant。

Value

template <typename T> T value();

从Variant获取原始类型T。

5. Serializer的设计

template<typename Writer>

struct Serializer

{

    void Serialize(const Variant &val);

    Writer output;

};

Writer决定序列化后的格式,可以为JsonWriter, XmlWriter等等,根据需要传递自己需要的序列化格式Writer。

5.1 JsonWriter示例

以JsonWriter演示如何将一个自定义类型CustomClass  obj序列化为一个json字符串。

序列化过程:

获取CustomClass的唯一的typeId;

根据typeId构造一个空Variant, CustomClass的typeId等于Varint的typeId;

new CustomClass对象,将其指针赋给Variant.data.value.ptr(见4.2);

在Variant.data.value.ptr这个地址上复制构造obj的对象数据,这样Variant中有跟obj一样的数据。

JsonWriter读取Variant数据;

通过Variant::typeId 获取TypeInfo;

将TypeInfo::name按一定规则写到json

遍历TypeInfo::properties,将每一个成员变量信息按一定规则propert写到json。

6. Deserializer的设计

template<typename ReadBuffer, typename Reader>

struct Deserializer

{

    static Variant Deserialize(ReadBuffer &rb);

};

参数rb是序列化的数据,通过Reader的规则从rb读取数据反序列化为Variant.

6.1 JsonReader示例

以JsonReader演示如何将一个json字符串反序列化为一个CustomClass对象  myClass。

反序列化过程:

Parse 序列化的json字符串为一个JsonValue对象jvobj;

解析jvobj, 获取类名字符串“CustomClass”;

通过类名获取CustomClass关联的TypeInfo;

通过TypeInfo构造一个Variant v;

变量json对象jvobj中“Value”key对应 的数组,该数组记录了所有成员变量信息;

通过变量名称字符串获取该成员变量的TypeId ,若该变量为内建类型,调用Variant::FromValue,转为Variant ;若该变量为自定义类类型,重复(3)处的步骤,通过变量名获取TypeInfo,构造Varint,遍历properties;

将该Variant设置到v对象上,该过程底层调用了CustomClass成员变量的Getter和Setter方法,设置成员变量的值。

将Variant转为CustomClass, 由于在步骤(3-7)就是在填充ptrs数据,Variant中的ptr指向的数据就是CustomClass对象数据,最后可以直接将Variant类型强转为CustomClass类型。

猜你喜欢

转载自blog.csdn.net/GWB_superman/article/details/106875464