C++如何通過Qt反射機(jī)制實(shí)現(xiàn)數(shù)據(jù)類序列化
在 C++ 工程中經(jīng)常需要使用數(shù)據(jù)類,并對(duì)數(shù)據(jù)類進(jìn)行存儲(chǔ)、打印、調(diào)試等操作。由于數(shù)據(jù)類中有大量數(shù)據(jù)字段,每次都編寫存儲(chǔ)或輸出數(shù)據(jù)內(nèi)容,工作重復(fù)量太大。C++ 不支持用戶自定義的注解,所以沒辦法使用類似 java 中類似 Lombok 的插件。但是 QT 的屬性系統(tǒng)和 moc 編譯系統(tǒng),為簡(jiǎn)化數(shù)據(jù)類的序列化提供了可能性。
設(shè)計(jì)預(yù)期
- 保持?jǐn)?shù)據(jù)類的簡(jiǎn)潔,最好通過類似 Lombok 的注解語法,只讓聲明序列化的數(shù)據(jù)字段支持序列化;
- 自動(dòng)生成 getter 和 setter 方法;
- 支持?jǐn)?shù)據(jù)流輸入/輸出;
- 支持 QDebug 直接輸出對(duì)象內(nèi)容;
- 能自動(dòng)解包 QPoint、QRect、QVariant 等結(jié)構(gòu)化數(shù)據(jù)。
設(shè)計(jì)思路
使用宏代理注解完成 getter 和 setter 方法的自動(dòng)生成;
使用反射系統(tǒng)結(jié)合宏定義記錄需要序列化的字段信息;
使用基類完成序列化的模板代碼,讓數(shù)據(jù)類繼承序列化基類即可完成數(shù)據(jù)的序列化。
代碼實(shí)現(xiàn)
以下代碼中 Serializable 是數(shù)據(jù)類的基類,在其 cpp 文件中內(nèi)置了許多模板代碼用于支持處理數(shù)據(jù)流及 QDebug 操作。宏 SERIALIZE(className) 用于指定需要序列化的類,宏JSONFIELD(field, alias, ...) 用于指定需要序列化的字段。為了保證數(shù)據(jù)類的輕量化,此處沒有使用 QObject 類,而是使用了輕量化的 Q_GADGET,這樣在預(yù)編譯時(shí)不會(huì)產(chǎn)生太多的代碼。
// serializable.h
#ifndef SERIALIZABLE_H
#define SERIALIZABLE_H
#include "qjsonobject.h"
#include <QObject>
#include <QMetaObject>
#include <QMetaProperty>
#include <QDebug>
#include <QFile>
#include <QtWidgets/QMessageBox>
// SERIALIZE 宏內(nèi)不能包含 Q_GADGET ,必須在子類中單獨(dú)使用 Q_GADGET,
// 因?yàn)?moc 時(shí)不引入其它頭文件,此處定義Q_GADGET對(duì)子類無效,子類不會(huì)生成moc文件
//const_cast<className&>(explicit
#define SERIALIZE(className) \
Q_CLASSINFO("base", "Serializable") \
public: \
className(const className &other){ \
copy(other, *this); \
} \
inline const QMetaObject* getMetaInfo() const override{ \
return &staticMetaObject; \
}
#ifndef Q_MOC_RUN
// define the tag text as empty, so the compiler doesn't see it
# define JSON_FLAG
#endif
#define VAR_TYPE(var) decltype(var)
#ifdef CHAIN
#define SETTER(field, alias) \
inline auto set##alias(const __type_##field &value) { \
field = value; \
return this; \
}
#else
#define SETTER(field, alias) \
inline void set##alias(const __type_##field &value) { \
field = const_cast<__type_##field>(value); \
}
#endif
#define JSONFIELD(field, alias, ...) \
using __type_##field = decltype(field) ;\
Q_PROPERTY(__type_##field field READ get##alias WRITE set##alias) \
public: \
Q_INVOKABLE JSON_FLAG inline QMap<QString, QString> __get##alias##Info__(){ \
QMap<QString, QString> info; \
info["name"] = #field; \
info["alias"] = #alias; \
info["args"] = QString(#__VA_ARGS__); \
return info; \
} \
inline __type_##field get##alias() const { return field; } \
inline void set##alias(const __type_##field &value) { \
field = value; \
}
class Serializable
{
public:
Serializable();
virtual ~Serializable();
static bool isSubClass(QMetaType type);
virtual const QMetaObject* getMetaInfo() const = 0;
void copy(const Serializable &from, Serializable &to);
void copy(const Serializable &from);
virtual QString hashCode();
virtual bool equals(Serializable &obj);
virtual bool operator==(Serializable &obj);
virtual QString toString();
template<typename T>
T invokeMethod(const QMetaObject *metaInfo, int index);
virtual QVariant getValue(QString fieldName);
virtual void setValue(QString fieldName, QVariant value);
friend QDataStream &operator<<(QDataStream &stream, const Serializable &data);
friend QDataStream &operator>>(QDataStream &stream, Serializable &data);
friend QDebug operator<<(QDebug dbg, const Serializable &data);
private:
QString id;
};
Q_DECLARE_METATYPE(Serializable)
#endif // SERIALIZABLE_H
由于 cpp 文件的代碼太多,此處不再貼出來,需要的請(qǐng)到項(xiàng)目 https://github.com/lsyeei/dashboard 的源碼目錄 /common/ 中查看 serializable.cpp
使用方法
- 子類繼承 Serializable
- 子類中須使用宏 Q_GADGET
- 子類中使用宏 SERIALIZE(className) 開啟序列化功能
- 子類中使用 JSONFIELD(field, alias, ...) 指定哪些字段需要序列化
- 使用Q_DECLARE_METATYPE(className)注冊(cè)元數(shù)據(jù)類型
說明:
- 使用 SERIALIZE 會(huì)自動(dòng)生成拷貝構(gòu)造函數(shù)
- 使用 JSONFIELD 指定的字段會(huì)自動(dòng)生成 get和set函數(shù),不必單獨(dú)聲明
舉例:
class Student : public Serializable
{
Q_GADGET
SERIALIZE(Student)
public:
Student();
~Student();
private:
QString name;
int age;
JSONFIELD(name, Name)
JSONFIELD(age, Age)
};
Q_DECLARE_METATYPE(Cunstom)
Student的使用方法:
Student student;
student.setName("li");
student.setAge(15);
qDebug() << student;
QByteArray array;
QDataStream stream(&array, QIODeviceBase::WriteOnly);
stream << student;
項(xiàng)目 Compelling Data Designer 用于數(shù)據(jù)的可視化設(shè)計(jì),軟件采用可擴(kuò)展架構(gòu),支持?jǐn)U展圖形插件、數(shù)據(jù)接口。項(xiàng)目仍在開發(fā)中,目前已設(shè)計(jì)完成基本圖形、多屬性配置、動(dòng)畫等功能。結(jié)合 Serializable 類,項(xiàng)目中還提供了 JSON 序列化的實(shí)現(xiàn)方式。具體介紹見: QT 實(shí)現(xiàn) C++ 數(shù)據(jù)類與 json 的轉(zhuǎn)換


以上就是C++如何通過Qt反射機(jī)制實(shí)現(xiàn)數(shù)據(jù)類序列化的詳細(xì)內(nèi)容,更多關(guān)于C++數(shù)據(jù)類序列化的資料請(qǐng)關(guān)注腳本之家其它相關(guān)文章!
相關(guān)文章
C++驗(yàn)證LeetCode包圍區(qū)域的DFS方法
這篇文章主要介紹了C++驗(yàn)證LeetCode包圍區(qū)域的DFS方法,本篇文章通過簡(jiǎn)要的案例,講解了該項(xiàng)技術(shù)的了解與使用,以下就是詳細(xì)內(nèi)容,需要的朋友可以參考下2021-07-07
C語言利用數(shù)組和文件實(shí)現(xiàn)登錄注冊(cè)功能
這篇文章主要為大家詳細(xì)介紹了C語言利用數(shù)組和文件實(shí)現(xiàn)登錄注冊(cè)功能,文中示例代碼介紹的非常詳細(xì),具有一定的參考價(jià)值,感興趣的小伙伴們可以參考一下2020-12-12
C++實(shí)現(xiàn)循環(huán)隊(duì)列
這篇文章主要為大家詳細(xì)介紹了C++實(shí)現(xiàn)循環(huán)隊(duì)列,文中示例代碼介紹的非常詳細(xì),具有一定的參考價(jià)值,感興趣的小伙伴們可以參考一下2020-01-01
Qt實(shí)現(xiàn)部件透明陰影效果與不規(guī)則窗體詳解
這篇文章主要為大家詳細(xì)介紹了Qt實(shí)現(xiàn)部件透明陰影效果與不規(guī)則窗體的相關(guān)方法,文中的示例代碼講解詳細(xì),感興趣的小伙伴可以了解一下2023-01-01
詳解如何用alpine鏡像做一個(gè)最小的鏡像并運(yùn)行c++程序
這篇文章主要介紹了詳解如何用alpine鏡像做一個(gè)最小的鏡像并運(yùn)行c++程序,文中通過示例代碼介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價(jià)值,需要的朋友們下面隨著小編來一起學(xué)習(xí)學(xué)習(xí)吧2020-10-10
MATLAB中subplot函數(shù)的語法與使用實(shí)例
subplot()是將多個(gè)圖畫到一個(gè)平面上的工具,下面這篇文章主要給大家介紹了關(guān)于MATLAB中subplot函數(shù)的語法與使用的相關(guān)資料,文中通過實(shí)例代碼介紹的非常詳細(xì),需要的朋友可以參考下2022-08-08

