AC——c++数据序列化方案

1 背景

AC(Auto Converter)是一款轻量级的基于c++开发的数据序列化开发框架。框架基于纯c++开发,开箱即用,无三方依赖库。由于c/c++语言没有类似java\python等到高级语言获取对象的metadata的功能, 在数据序列化时需要针对具体的数据对象开发序列化代码,开发起来比较复杂且容易出错。

AC框架借鉴java中Object的思想,定义超类AutoConverter,其他所有类均从此类派生,用于保存数据和调用序列化操作。为了能够产生多种序列化类型的数据如json、xml、c二进制数据,将序列化操作使用Serializer类抽象。数据序列化过程可分为:数据约束定义、序列化。

数据约束定义时用户描述数据的逻辑结构,比如c/c++程序员定义struct\union的过程实际上就是进行约束定义的过程。当程序编译连接运行起来后,向struct写入数据则可以看做数据序列化的过程。c/c++是强类型语言,这表示用户只有在确定数据类型时才能正确访问内存中的数据,否则会导致程序之后的行为无法预料,即任何c/c++语言对数据的操作都是通过明确代码指定的。因为c/c++语言编译器不维护对象的metadata,所以程序在运行过程中不能像java或者python解释器一样,可以遍历对象的成员,获取成员对象类型。这使得c/c++程序很难利用编译器这一层的特性实现序列化(有这种特性的高级语言往往天然支持数据序列化,大大简化开发)

既然很难利用编译,为了实现序列化功能,最简单的方案是针对每一个用户想要序列化的开发单独的序列化代码,比如:

struct STest
{
    int a;
    float b;
    char c[8];
};

std::string STest2json(struct STest & stru)
{
    std::string strOut;
    strOut += "{\"a\":" + to_string(stru.a) + ",";
    strOut +=  "\"b\":" + to_string(stru.b) + ",";
    strOut += "\"c\":" + stru.c + "}";
    return strOut;
}

这种方案的缺点非常明显,有多少需要序列化的结构体,就要开发多少个序列化函数,工作量大,且极容易出错。

2 方案介绍

上文提到,c/c++编译器不维护代码中结对象的metadata,所以c++程序无法像java一样利用编译器提供的元数据遍历对象的成员、获取成员的类型。那有没有办法在应用层实现这一功能呢?答案是有的,qt中利用元编译器moc,在代码进入编译阶段前先完成元编译,生成包含对象元数据的代码,然后一同编译到程序中,从而实现类似java对象元数据的功能。元编译器的关键在于,解释用户定义的struct对象结构、成员类型等,产生元数据,此过程需要借助编译前端产生的语法分析树(qt使用了clang)。数据序列化时,则可以通过遍历对象的元数据,逐个转换成员。

方案优点:1.用户开发量少,元数据产生由元编译器完成,序列化操作由框架完成;2.不改变用户代码中原有数据类型的定义
方案缺点:1.元编译器开发复杂

元编译器开发的复杂性超过数据序列化问题的本身,专门为了实现数据序列化而开发元编译器是得不偿失的。

通过分析c复合数据类型、json和xml格式,其逻辑结构都属于树状结构。(更复杂的网状数据序列化后是很难表现数据间关系的)于是AC通过定义超类AutoConverter,使所有数据类型均派生于超类,并提供Serialize接口,实现类似MFC CObject, java Object类的功能。

2.1 概要设计

系统的类图

 

2.2 使用说明

约束定义

example-1

复合数据类型AC_Struct定义

#include "AC.hpp"
struct AC_Test1 : public AC_Struct
{
public:
	AC_Test1(const char* name) : AC_Struct(name, PLACE_HOLDER)
	{
		m_val = {
			AC_Mem(AC_Number<int>, ("parm0", 10)),
			AC_Mem(AC_Char, ("parm1", 'A')),
			AC_Mem(AC_Number<double>, ("parm2", 1.204)),
		};
	}
	AC_Test1(const AC_Test1 & other) : AC_Struct(other)
	{
	}
	~AC_Test1() {}
};

int main()
{
	AC_Test1 test1("test1");
	std::cout << "**** test1 *****\n\n" << AC_To_Json(test1) << std::endl << std::endl;
}

example-2

列表数据类型AC_Array定义

#include "AC.hpp"
struct AC_Test3 : public AC_Array
{
public:
	AC_Test3(const char* name) : AC_Array(name, PLACE_HOLDER)
	{
		AC_Test1 parm4("parm4");
		GET(GET(a, AC_Number<int>, "parm0"), int, "") = 298;
		m_val = {
			AC_Mem(AC_Number<int>, ("parm1", 10)),
			AC_Mem(AC_Number<double>, ("parm2", 1.204)),
			AC_Mem(AC_Boolean, ("parm3", true)),
			AC_Mem(AC_Test1, (parm4)),
		};
	}
	~AC_Test3() {}
};

int main()
{
	AC_Test3 test3("test3");
	std::cout << "**** test3 *****\n\n" << AC_To_Json(test3) << std::endl << std::endl;
}

运行结果

项目Git路径:https://github.com/csjy309450/AC.git

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章