当前位置: 首页 > article >正文

基于QT的C++中小项目软件开发架构源码

描述

基于QT信号槽机制实现类之间的交互调用通信,适用于使用不同枚举作为消息交互的类型场景,支持附带任意参数,代码使用方式参考前一篇文章

特性
  1. 代码简洁,不超过100行
  2. 仅需包含一个头文件Communicator.h,需要通信的业务类继承Communicator,界面类继承VCommunicatorMVCommunicator
  3. 消息通过枚举类型定义,支持任意枚举消息,添加枚举无需改动代码。枚举名称区分消息类型,枚举值区分具体消息
  4. 使用便捷,只需注册消息和消息处理函数
  5. 提供发送消息时,附带any传递任意额外信息
  6. 支持多线程,同QT一致的信号槽触发方式,如需控制信号槽触发方式,需要额外补充代码
对比QT信号槽
  1. 适用于使用不同枚举作为消息类型场景,支持附带任意参数
  2. 使用同QT一样便捷,只需注册消息和消息回调函数。且如果使用QT的信号槽进行通信,不同的消息的类型则需要定义多个connect连接函数,如ELoginMsg、ERunMsg,多一种消息就要多一个connect

Communicator.h

#pragma once
#include <QMainWindow>
#include <functional>
#include <unordered_map>
#include <any>

class MsgHandler {
public:
	using MsgID = size_t;
	using MsgHandleFunc = std::function<void(std::any)>;
	using MsgHandleFuncNoPara = std::function<void()>;
	template<typename T, typename = std::enable_if_t<std::is_enum<T>::value, void>>
	void HandleMsg(T msg, std::any info = std::any{}) {
		m_msgHandlers[HashID(msg)](info);
	}
protected:
	template<typename T, typename = std::enable_if_t<std::is_enum<T>::value, void>>
	MsgID HashID(T msg) {
		return std::hash<std::string>()(typeid(T).name()) ^ std::hash<size_t>()(static_cast<size_t>(msg));
	}
	template<typename T, typename = std::enable_if_t<std::is_enum<T>::value, void>>
	void RegisterMsgHandler(T msg, MsgHandleFuncNoPara func) {
		m_msgHandlers.insert({ HashID(msg),[=] (std::any) { func(); } });
	}
	template<typename T, typename = std::enable_if_t<std::is_enum<T>::value, void>>
	void RegisterMsgHandler(T msg, MsgHandleFunc func) {
		m_msgHandlers.insert({ HashID(msg), func });
	}
	template<typename T, typename = std::enable_if_t<std::is_enum<T>::value, void>>
	void RemoveMsgHandler(T msg) {
		if (auto findit = m_msgHandlers.find(msg); findit != m_msgHandlers.end()) {
			m_msgHandlers.erase(findit);
		}
	}
protected:
	std::unordered_map<MsgID, MsgHandleFunc> m_msgHandlers;
};

#define DEF_COMMUNICATOR_CLASS(CommClassName, QtClassName)											\
class CommClassName : public QtClassName, public MsgHandler {										\
	Q_OBJECT																						\
public:																								\
	CommClassName(QtClassName* parent = nullptr) : QtClassName(parent) {							\
		qRegisterMetaType<MsgID>("MsgID");															\
		qRegisterMetaType<std::any>("std::any");													\
	}																								\
	Q_SIGNAL void SendMsg(MsgID, std::any);															\
public:																								\
	Q_SLOT void HandleHashMsg(MsgID msgID, std::any info = std::any{}) {							\
		if (bool findHandler = m_msgHandlers.find(msgID) != m_msgHandlers.end()) {					\
			m_msgHandlers[msgID](info);																\
		}																							\
	}																								\
public:																								\
	template<typename Receiver>																		\
	void Connect(Receiver* receiver) {																\
		connect(this, &CommClassName::SendMsg, receiver, &Receiver::HandleHashMsg);					\
	}																								\
	template<typename Receiver>																		\
	void Disconnect(Receiver* receiver) {															\
		disconnect(this, &CommClassName::SendMsg, receiver, &Receiver::HandleHashMsg);				\
	}																								\
protected:																							\
	template<typename T, typename = std::enable_if_t<std::is_enum<T>::value, void>>					\
	void Emit(T msg, std::any info = nullptr) {														\
		emit SendMsg(HashID(msg), info);															\
	}																								\
}				
DEF_COMMUNICATOR_CLASS(Communicator, QObject);
DEF_COMMUNICATOR_CLASS(ViewCommunicator, QWidget);
DEF_COMMUNICATOR_CLASS(MViewCommunicator, QMainWindow);

namespace InterConnection {
template<typename Communicator1, typename Communicator2>
void Connect(Communicator1* c1, Communicator2* c2)
{
	c1->Connect(c2);
	c2->Connect(c1);
}

template<typename Communicator1, typename Communicator2>
void Disconnect(Communicator1* c1, Communicator2* c2)
{
	c1->Disconnect(c2);
	c2->Disconnect(c1);
}
}
补充

c++17才支持any,C++17之前的版本可以使用下面的any.h,判断版本大于c++17可以通过宏__cplusplus >= 201703L
any.h

#include <typeinfo>
#include <memory>
namespace std {
class any
{
	class AnyHelperBase
	{
	public:
		virtual const std::type_info& type()const = 0;
		virtual AnyHelperBase* clone()const = 0;
	};

	template<typename T>
	class AnyHelper :public AnyHelperBase
	{
	public:
		T data;
		template<typename ...Args>
		AnyHelper(Args&&... args) :data(std::forward<Args>(args)...) {}
		AnyHelper(const AnyHelper& rhs) :data(rhs.data) {}
		AnyHelper(const T& value) :data(value) {}
		virtual const std::type_info& type() const
		{
			return typeid(T);
		}
		virtual AnyHelper* clone() const
		{
			return new AnyHelper(*this);
		}
	};
	template<typename T>
	friend T any_cast(const any& a);
private:
	std::unique_ptr<AnyHelperBase> pdata{};
public:
	any() :pdata(nullptr) {}
	template<typename T>
	any(T&& value) : pdata(new AnyHelper<std::decay_t<T>>(value)) {}
	any(const any& rhs) {
		if (rhs.pdata != nullptr) {
			pdata.reset(rhs.pdata->clone());
		}
	}
	any(any& rhs) {
		if (rhs.pdata != nullptr) {
			pdata.reset(rhs.pdata->clone());
		}
	}
	any(any&& rhs) :pdata(rhs.pdata.release()) {}

	const std::type_info& type() const
	{
		return pdata->type();
	}
	bool has_value() const {
		return pdata != nullptr;
	}
	void reset() {
		pdata.reset();
	}
	template<typename T>
	any& operator=(T value) {
		pdata.reset(new AnyHelper<std::decay_t<T>>(value));
		return *this;
	}
	any& operator=(any rhs)
	{
		pdata.reset(rhs.pdata->clone());
		return *this;
	}
};

template<typename T>
T any_cast(const any& a)
{
	auto p = dynamic_cast<any::AnyHelper<std::decay_t<T>>*>(a.pdata.get());
	if (p == nullptr)
		throw std::runtime_error("Bad any cast!");
	return p->data;
}
}

由于现在大多软件都是基于QT进行开发,QT又有自己的事件循环机制,所以只能进行拓展通信方式,也可以自行实现事件循环,但是跟QT结合起来用就不太适宜了。


http://www.kler.cn/news/322629.html

相关文章:

  • oracle生成时间戳字符的两种方法
  • 教师管理系统小程序+ssm论文源码调试讲解
  • 什么是数据倾斜
  • LeetCode[简单] 136. 只出现一次的数字
  • 网络:TCP协议-报头字段
  • webpack 4 的 30 个步骤构建 react 开发环境
  • mysql复合查询 -- 多表查询(介绍,笛卡尔积,使用),自连接(介绍,使用)
  • MySQL tinyint(1)类型数据在经过flink cdc同步到doris后只有0/1问题定位与解决
  • C#自定义工具类-数组工具类
  • XML 实例:深入解析与实际应用
  • 如何从格式化的笔记本电脑或台式机中恢复照片
  • C++进阶—>这3个问题难道搞不懂多态???
  • 对Spring-AI系列源码的讲解
  • 线性判别分析 (LDA)中目标函数对S_w进行奇异值分解的说明
  • 如何在Mac上查看剪贴板历史记录
  • 数据技术进化史:从数据仓库到数据中台再到数据飞轮的旅程
  • python示例(加减乘除....)
  • Hive SQL业务场景:连续5天涨幅超过5%股票
  • 排序--堆排序【图文详解】
  • C++入门day5-面向对象编程(终)
  • 【鸿蒙】HarmonyOS NEXT开发快速入门教程之ArkTS语法装饰器(下)
  • Oracle 启动对应数据库实例数据库方法
  • Golang优雅关闭gRPC实践
  • 【CSS in Depth 2 精译_041】6.4 CSS 中的堆叠上下文与 z-index(上)
  • 短剧向左,体育向右,快手前途未卜?
  • Python爬虫之urllib模块详解
  • 通过 GitLab API 实现 CHANGELOG.md 文件的自动化上传至指定分支
  • GS-SLAM论文阅读笔记--GLC-SLAM
  • 3D建模:Agisoft Metashape Professional 详细安装教程分享 Mac/win
  • Word:表格公式计算