C++如何通过Qt反射机制实现数据类序列化
目录
- 设计预期
- 设计思路
- 代码实现
- 使用方法
在 C++ 工程中经常需要使用数据类,并对数据类进行存储、打印、调试等操作。由于数据类中有大量数据字段,每次都编写存储或输出数据内容,工作重复量太大。C++ 不支持用户自定义的注解,所以没办法使用类似 Java 中类似 Lombok 的插件。但是 QT 的属性系统和 moc 编译系统,为简化数据类的序列化提供了可能性。
设计预期
- 保持数据类的简洁,最好通过类似 Lombok 的注解语法,只让声明序列化的数据字段支持序列化;
- 自动生成 getter 和 setter 方法;
- 支持数据流输入/输出;
- 支持 QDebug 直接输出对象内容;
- 能自动解包 QPoint、QRect、QVariant 等结构化数据。
设计思路
使用宏代理注解完成 getter 和 setter 方法的自动生成;
使用反射系统结合宏定义记录需要序列化的字段信息;
使用基类完成序列化的模板代码,让数据类继承序列化基类即可完成数据的序列化。
代码实现
以下代码中 Serializable 是数据类的基类,在其 cpp 文件中内置了许多模板代码用于支持处理数据流及 QDebug 操作。宏 SERIALIZE(className) 用于指定需要序列化的类,宏jsONFIELD(field, alias, ...) 用于指定需要序列化的字段。为了保证数据类的轻量化,此处没有使用 QObject 类,而是使用了轻量化的 Q_GADGET,这样在预编译时不会产生太多的代码。
// 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 宏内不能包含 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 &jsfrom, 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>>(QjsDataStream &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)注册元数据类型
说明:
- 使用 SERIALIZE 会自动生成拷贝构造函数
- 使用 JSONFIELD 指定的字段会自动生成 get和set函数,不必单独声明举例:
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 用于数据的可视化设计,软件采用可扩展架构,支持扩展图形插件、数据接口。项目仍在开发中,目前已设计完成基本图形、多属性配置、动画等功能。结python合 Serializable 类,项目中还提供了 JSON 序列化的实现方式。具体介绍见: QT 实现 C++ 数据类与 json 的转换
以上就是C++如何通过Qt反射机制实现数据类序列化的详细内容,更多关于C++数据类序列化的资料请关注编程客栈(www.devze.com)其它相关文章!
精彩评论