借助 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 &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)注册元数据类型
说明:
- 使用 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 用于数据的可视化设计,软件采用可扩展架构,支持扩展图形插件、数据接口。项目仍在开发中,目前已设计完成基本图形、多属性配置、动画等功能。结合 Serializable 类,项目中还提供了 JSON 序列化的实现方式。
借助 QT 的反射机制实现数据类的序列化的更多相关文章
- Android利用反射机制为实体类属性赋值
在做android项目时,有时会遇到从网络上获取json类型数据,赋值给实体类,实体类属性少可以一个一个的赋值,如果实体类有很多属性,赋值可能就要耗很长的功夫了,幸好Java给我们提供了反射机制.下面 ...
- java 通过反射机制调用某个类的方法
package net.xsoftlab.baike; import java.lang.reflect.Method; public class TestReflect { public s ...
- 利用Java反射机制对实体类的常用操作工具类ObjectUtil
代码: ObjectUtil类: import java.lang.reflect.Field; import java.math.BigDecimal; import java.text.Simpl ...
- 浅谈Java反射机制 之 使用类的 属性、方法和构造函数
前面两篇我们总结了Java反射机制如何获取类的字节码,如何获取构造函数,属性和方法, 这篇我们将进一步验证如何使用我们获取到的属性.方法以及构造函数 1.使用 反射 获取到的 属性 import ja ...
- Day14_82_反射机制输出整个类
利用反射机制输出整个类 代码实例 import java.lang.reflect.Field; import java.lang.reflect.Modifier; public class Ref ...
- 深入Qt 学习 -- 反射机制(比较简单清楚)
相对于Java天生的这一特性, C++并不具备;但进入到Qt领域,这一切都变得简单自如了. 从Qt的元对象系统可知,除了提供信号/槽机制的特性之外,它还提供了以下特性: ■ QObject::meta ...
- c#反射机制判断同一个类的两个实例的值是否完全一样
; i < properties1.Length; i++) { string s = properties1[i].DeclaringTyp ...
- JAVA反射机制教程-获取类对象
1. 什么是类对象 类对象,就是用于描述这种类,都有什么属性,什么方法的 2. 获取类对象 获取类对象有3种方式(1). Class.forName(2). Hero.class(3). new He ...
- 浅谈Java反射机制 之 获取类的字节码文件 Class.forName("全路径名") 、getClass()、class
另一个篇:获取 类 的 方法 和 属性(包括构造函数) 先贴上Java反射机制的概念: AVA反射机制是在运行状态中,对于任意一个类,都能够知道这个类的所有属性和方法: 对于任意一个对象,都能够调用它 ...
- Qt之类反射机制
在java语言中,可以使用getObject(String)函数,从类型直接构建新的对象. 而在C++中是没有这种机制的,Qt虽然提供了元对象机制,但只可以获取对象的类名,不能反向构建. 所以搜索一下 ...
随机推荐
- ThreeJs-14HTML混合3D渲染
一.css渲染器基本使用 使用场景:经常在一些3D物体左右还会跟随一些文字等,实际都是标签,应用场景非常广泛 先完成基本的3D,月球围绕地球转,这里就是给月球加了sin函数 然后现在就先创建一个标签体 ...
- Python介绍以及应用场景
Python介绍以及应用场景 Python简介 Python,作为一种解释型.面向对象的高级编程语言,自1989年圣诞节期间由荷兰计算机科学家Guido van Rossum创造以来,已经取得了举 ...
- vue集成amis
一.下载amis 方式一:git下载sdk.tar.gz,https://github.com/baidu/amis/releases 方式二:npm i amis 来下载,在 node_module ...
- [BZOJ2194] 快速傅立叶之二 题解
看名字,然后准备转化为多项式乘法. \[c_k=\sum_{i=0}^{n-k-1}a_{i+k}b_i \] 将 \(a\) 反转,得: \[c_k=\sum_{i=0}^{n-k-1}a_{n-i ...
- 几乎全平台的C语言JSON解析工具cJSON[转载]
最近在做一个外设管理平台,用PYTHON写了一个连接管理,兼容串口和套接字的连接,然后抽象为设备统一管理.使用套接字时JSON是一种很好的数据封装类型,假设我需要远程操控一个设备,发送一个JSON的数 ...
- java http协议,添加header以及post传参,以及服务端获取
一.客户端请求 public static String test(){ JSONObject obj = new JSONObject(); obj.put("cusName", ...
- 【最佳方案】RAG 技术深度剖析及 MaxKB 在企业 AI 落地中的应用策略
RAG 已经成为 LLM 大语言模型在企业落地的最佳方案,其中主要是因为 RAG 能够解决幻觉问题.时效性问题以及数据安全问题. 解决幻觉问题: LLM 文本生成的底层原理是基于概率的 token b ...
- 个人文件转移工具-来自某位大神的C盘清理神器
软件名称:个人文件转移工具 软件功能:文件转移 支持平台:Windows 软件简介:一款文件转移工具,也可用作C盘瘦身. 软件特点: ◉ "个人文件转移工具"可以把"我的 ...
- Edge、谷歌浏览器默认下载器开启多线程下载
浏览器默认下载器开启多线程下载 Chrome 浏览器,地址栏输入并回车: chrome://flags/#enable-parallel-downloading Edge 新版浏览器,地址栏输入并回车 ...
- 7. Nginx 工作机制&参数设置(详细讲解说明)
7. Nginx 工作机制&参数设置(详细讲解说明) @ 目录 7. Nginx 工作机制&参数设置(详细讲解说明) 1. Nginx 当中的 master-worker 机制原理 2 ...