在 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,这样在预编译时不会产生太多的代码。
[code]// serializable.h#ifndef SERIALIZABLE_H#define SERIALIZABLE_H#include "qjsonobject.h"#include #include #include #include #include #include // SERIALIZE 宏内不能包含 Q_GADGET ,必须在子类中单独使用 Q_GADGET,// 因为 moc 时不引入其它头文件,此处定义Q_GADGET对子类无效,子类不会生成moc文件//const_cast(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(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 __get##alias##Info__(){ \ QMap 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 T invokeMethod(const QMetaObject *metaInfo, int index); virtual QVariant getValue(QString fieldName); virtual void setValue(QString fieldName, QVariant value); friend QDataStream &operator(QDataStream &stream, Serializable &data); friend QDebug operator |