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

描述

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

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

Communicator.h

#pragma once
#include <functional>
#include <unordered_map>
#include <map>
#include "any.h"

struct EnumMsg {
	EnumMsg() = default;
	template<typename T, typename = std::enable_if_t<std::is_enum<T>::value>>
	EnumMsg(T msg) : type_(typeid(T).name()),val_(static_cast<int>(msg)) {}
	bool operator==(const EnumMsg& e1) const noexcept { return e1.type_ == type_ && e1.val_ == val_; }
	const char* type_;
	int val_;
};
template<>
struct std::hash<EnumMsg> {	
	size_t operator()(const EnumMsg& e1)const noexcept { return std::hash<const char*>()(e1.type_) ^ std::hash<int>()(e1.val_); }
};

class MsgHandler {
public:
	void HandleMsg(EnumMsg msg, std::any info = std::any{}) {
		if (bool findHandler = msgHandlers_.find(msg) != msgHandlers_.end()) {
			msgHandlers_[msg](info);
			return;
		}
		if (bool findTypeHandler = typeHandlers_.find(msg.type_) != typeHandlers_.end()) {
			typeHandlers_[msg.type_](msg.val_);
		}
	}
protected:
	void RegisterMsgHandler(EnumMsg msg, std::function<void()> func) {
		msgHandlers_.insert({ msg,[=](std::any) { func(); } });
	}
	void RegisterMsgHandler(EnumMsg msg, std::function<void(std::any)> func) {
		msgHandlers_.insert({ msg, func });
	}
	template<typename T, typename = std::enable_if_t<std::is_enum<T>::value>>
	void RegisterTypeHandler(std::function<void(std::any)> func) {
		typeHandlers_.insert({ typeid(T).name(), func });
	}
protected:
	std::unordered_map<EnumMsg, std::function<void(std::any)>> msgHandlers_;
	std::unordered_map<const char*, std::function<void(std::any)>> typeHandlers_;
};

class ModuleCommunicator {
public:
	static ModuleCommunicator* GetInstance() {
		static ModuleCommunicator i;
		return &i;
	}
	void Emit(EnumMsg moduleID, EnumMsg msg, std::any info = std::any{}) {
		for (auto& communicator : moduleCommunicator_[moduleID]) {
			communicator.second->HandleMsg(msg, info);
		}
	}
	void RegisterCommunicator(EnumMsg moduleID, MsgHandler* communicator, int priority = 0) {
		moduleCommunicator_[moduleID].insert({ priority, communicator });
	}
private:
	ModuleCommunicator() = default;
	std::unordered_map<EnumMsg, std::multimap<int, MsgHandler*, std::greater<int>>> moduleCommunicator_;
};
#define GModuleCommunicator ModuleCommunicator::GetInstance()

#define Q_ENUM_SIGSLOT(ClassName)													\
	Q_SIGNAL void SendMsg(EnumMsg, std::any);										\
public:																				\
	Q_SLOT void HandleHashMsg(EnumMsg msgID, std::any info = std::any{}) {			\
		if (bool findHandler = msgHandlers_.find(msgID) != msgHandlers_.end()) {	\
			msgHandlers_[msgID](info);												\
		}																			\
	}																				\
public:																				\
	template<typename Receiver>														\
	void Connect(Receiver* receiver) {												\
		connect(this, &ClassName::SendMsg, receiver, &Receiver::HandleHashMsg);		\
	}																				\
	template<typename Receiver>														\
	void Disconnect(Receiver* receiver) {											\
		disconnect(this, &ClassName::SendMsg, receiver, &Receiver::HandleHashMsg);	\
	}																				\
protected:																			\
	void Emit(EnumMsg msg, std::any info = nullptr) {								\
		emit SendMsg(msg, info);													\
	}																				\
	void Forward(EnumMsg msg) {														\
		RegisterMsgHandler(msg, [=](std::any info) {Emit(msg, info); });			\
	}
补充

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;
		virtual ~AnyHelperBase(){}
	};

	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结合起来用就不太适宜了。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

flower980323

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值