C++如何通過Qt反射機制實現(xiàn)數(shù)據(jù)類序列化
在 C++ 工程中經(jīng)常需要使用數(shù)據(jù)類,并對數(shù)據(jù)類進行存儲、打印、調(diào)試等操作。由于數(shù)據(jù)類中有大量數(shù)據(jù)字段,每次都編寫存儲或輸出數(shù)據(jù)內(nèi)容,工作重復(fù)量太大。C++ 不支持用戶自定義的注解,所以沒辦法使用類似 java 中類似 Lombok 的插件。但是 QT 的屬性系統(tǒng)和 moc 編譯系統(tǒng),為簡化數(shù)據(jù)類的序列化提供了可能性。
設(shè)計預(yù)期
- 保持數(shù)據(jù)類的簡潔,最好通過類似 Lombok 的注解語法,只讓聲明序列化的數(shù)據(jù)字段支持序列化;
- 自動生成 getter 和 setter 方法;
- 支持數(shù)據(jù)流輸入/輸出;
- 支持 QDebug 直接輸出對象內(nèi)容;
- 能自動解包 QPoint、QRect、QVariant 等結(jié)構(gòu)化數(shù)據(jù)。
設(shè)計思路
使用宏代理注解完成 getter 和 setter 方法的自動生成;
使用反射系統(tǒng)結(jié)合宏定義記錄需要序列化的字段信息;
使用基類完成序列化的模板代碼,讓數(shù)據(jù)類繼承序列化基類即可完成數(shù)據(jù)的序列化。
代碼實現(xiàn)
以下代碼中 Serializable 是數(shù)據(jù)類的基類,在其 cpp 文件中內(nèi)置了許多模板代碼用于支持處理數(shù)據(jù)流及 QDebug 操作。宏 SERIALIZE(className) 用于指定需要序列化的類,宏JSONFIELD(field, alias, ...) 用于指定需要序列化的字段。為了保證數(shù)據(jù)類的輕量化,此處沒有使用 QObject 類,而是使用了輕量化的 Q_GADGET,這樣在預(yù)編譯時不會產(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 ,必須在子類中單獨使用 Q_GADGET, // 因為 moc 時不引入其它頭文件,此處定義Q_GADGET對子類無效,子類不會生成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 文件的代碼太多,此處不再貼出來,需要的請到項目 https://github.com/lsyeei/dashboard 的源碼目錄 /common/ 中查看 serializable.cpp
使用方法
- 子類繼承 Serializable
- 子類中須使用宏 Q_GADGET
- 子類中使用宏 SERIALIZE(className) 開啟序列化功能
- 子類中使用 JSONFIELD(field, alias, ...) 指定哪些字段需要序列化
- 使用Q_DECLARE_METATYPE(className)注冊元數(shù)據(jù)類型
說明:
- 使用 SERIALIZE 會自動生成拷貝構(gòu)造函數(shù)
- 使用 JSONFIELD 指定的字段會自動生成 get和set函數(shù),不必單獨聲明
舉例:
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;
項目 Compelling Data Designer 用于數(shù)據(jù)的可視化設(shè)計,軟件采用可擴展架構(gòu),支持擴展圖形插件、數(shù)據(jù)接口。項目仍在開發(fā)中,目前已設(shè)計完成基本圖形、多屬性配置、動畫等功能。結(jié)合 Serializable 類,項目中還提供了 JSON 序列化的實現(xiàn)方式。具體介紹見: QT 實現(xiàn) C++ 數(shù)據(jù)類與 json 的轉(zhuǎn)換
以上就是C++如何通過Qt反射機制實現(xiàn)數(shù)據(jù)類序列化的詳細內(nèi)容,更多關(guān)于C++數(shù)據(jù)類序列化的資料請關(guān)注腳本之家其它相關(guān)文章!
相關(guān)文章
Qt實現(xiàn)部件透明陰影效果與不規(guī)則窗體詳解
這篇文章主要為大家詳細介紹了Qt實現(xiàn)部件透明陰影效果與不規(guī)則窗體的相關(guān)方法,文中的示例代碼講解詳細,感興趣的小伙伴可以了解一下2023-01-01