找回密码
 立即注册
首页 业界区 业界 借助 QT 的反射机制实现 C++ 数据类的序列化 ...

借助 QT 的反射机制实现 C++ 数据类的序列化

慕疼 5 天前
在 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
您需要登录后才可以回帖 登录 | 立即注册