C++如何通过Qt反射机制实现数据类序列化

2025-04-24 05:50

本文主要是介绍C++如何通过Qt反射机制实现数据类序列化,希望对大家解决编程问题提供一定的参考价值,需要的开发者们随着小编来一起学习吧!

《C++如何通过Qt反射机制实现数据类序列化》在C++工程中经常需要使用数据类,并对数据类进行存储、打印、调试等操作,所以本文就来聊聊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()China编程 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++如何通过Qt反射机制实现数据类序列化

以上就是C++如何通过Qt反射机制实现数据类序列化的详细内容,更多关于C++数据类序列化的资料请关注编程China编程(www.chinasem.cn)其它相关文章!

这篇关于C++如何通过Qt反射机制实现数据类序列化的文章就介绍到这儿,希望我们推荐的文章对编程师们有所帮助!



http://www.chinasem.cn/article/1154345

相关文章

SpringBoot集成redisson实现延时队列教程

《SpringBoot集成redisson实现延时队列教程》文章介绍了使用Redisson实现延迟队列的完整步骤,包括依赖导入、Redis配置、工具类封装、业务枚举定义、执行器实现、Bean创建、消费... 目录1、先给项目导入Redisson依赖2、配置redis3、创建 RedissonConfig 配

SpringBoot分段处理List集合多线程批量插入数据方式

《SpringBoot分段处理List集合多线程批量插入数据方式》文章介绍如何处理大数据量List批量插入数据库的优化方案:通过拆分List并分配独立线程处理,结合Spring线程池与异步方法提升效率... 目录项目场景解决方案1.实体类2.Mapper3.spring容器注入线程池bejsan对象4.创建

PHP轻松处理千万行数据的方法详解

《PHP轻松处理千万行数据的方法详解》说到处理大数据集,PHP通常不是第一个想到的语言,但如果你曾经需要处理数百万行数据而不让服务器崩溃或内存耗尽,你就会知道PHP用对了工具有多强大,下面小编就... 目录问题的本质php 中的数据流处理:为什么必不可少生成器:内存高效的迭代方式流量控制:避免系统过载一次性

Python的Darts库实现时间序列预测

《Python的Darts库实现时间序列预测》Darts一个集统计、机器学习与深度学习模型于一体的Python时间序列预测库,本文主要介绍了Python的Darts库实现时间序列预测,感兴趣的可以了解... 目录目录一、什么是 Darts?二、安装与基本配置安装 Darts导入基础模块三、时间序列数据结构与

C++右移运算符的一个小坑及解决

《C++右移运算符的一个小坑及解决》文章指出右移运算符处理负数时左侧补1导致死循环,与除法行为不同,强调需注意补码机制以正确统计二进制1的个数... 目录我遇到了这么一个www.chinasem.cn函数由此可以看到也很好理解总结我遇到了这么一个函数template<typename T>unsigned

Python使用FastAPI实现大文件分片上传与断点续传功能

《Python使用FastAPI实现大文件分片上传与断点续传功能》大文件直传常遇到超时、网络抖动失败、失败后只能重传的问题,分片上传+断点续传可以把大文件拆成若干小块逐个上传,并在中断后从已完成分片继... 目录一、接口设计二、服务端实现(FastAPI)2.1 运行环境2.2 目录结构建议2.3 serv

C#实现千万数据秒级导入的代码

《C#实现千万数据秒级导入的代码》在实际开发中excel导入很常见,现代社会中很容易遇到大数据处理业务,所以本文我就给大家分享一下千万数据秒级导入怎么实现,文中有详细的代码示例供大家参考,需要的朋友可... 目录前言一、数据存储二、处理逻辑优化前代码处理逻辑优化后的代码总结前言在实际开发中excel导入很

SpringBoot+RustFS 实现文件切片极速上传的实例代码

《SpringBoot+RustFS实现文件切片极速上传的实例代码》本文介绍利用SpringBoot和RustFS构建高性能文件切片上传系统,实现大文件秒传、断点续传和分片上传等功能,具有一定的参考... 目录一、为什么选择 RustFS + SpringBoot?二、环境准备与部署2.1 安装 RustF

Nginx部署HTTP/3的实现步骤

《Nginx部署HTTP/3的实现步骤》本文介绍了在Nginx中部署HTTP/3的详细步骤,文中通过示例代码介绍的非常详细,对大家的学习或者工作具有一定的参考学习价值,需要的朋友们下面随着小编来一起学... 目录前提条件第一步:安装必要的依赖库第二步:获取并构建 BoringSSL第三步:获取 Nginx

MyBatis Plus实现时间字段自动填充的完整方案

《MyBatisPlus实现时间字段自动填充的完整方案》在日常开发中,我们经常需要记录数据的创建时间和更新时间,传统的做法是在每次插入或更新操作时手动设置这些时间字段,这种方式不仅繁琐,还容易遗漏,... 目录前言解决目标技术栈实现步骤1. 实体类注解配置2. 创建元数据处理器3. 服务层代码优化填充机制详