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

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