自学内容网 自学内容网

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

描述

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

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

Communicator.h

#pragma once
#include <QMainWindow>
#include <functional>
#include <unordered_map>
#if __cplusplus >= 201703L
#include <any>
#else 
#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;
}
}
#endif

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;
};

class Communicator : public QObject, public MsgHandler {
Q_OBJECT
public:
Communicator(QObject* parent = nullptr) : QObject(parent) {
qRegisterMetaType<MsgID>("MsgID");
qRegisterMetaType<std::any>("std::any");
}
signals:
void SendMsg(MsgID, std::any);
public:
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 Communicator1>
void Connect(Communicator1* receiver) {
connect(this, &Communicator::SendMsg, receiver, &Communicator1::HandleHashMsg);
}
template<typename Communicator1>
void Disconnect(Communicator1* receiver) {
disconnect(this, &Communicator::SendMsg, receiver, &Communicator1::HandleHashMsg);
}
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);
}
};

class ViewCommunicator : public QWidget, public MsgHandler {
Q_OBJECT
public:
ViewCommunicator(QWidget* parent = nullptr) : QWidget(parent) {
qRegisterMetaType<MsgID>("MsgID");
qRegisterMetaType<std::any>("std::any");
}
signals:
void SendMsg(MsgID, std::any);
public:
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 Communicator1>
void Connect(Communicator1* receiver) {
connect(this, &ViewCommunicator::SendMsg, receiver, &Communicator1::HandleHashMsg);
}
template<typename Communicator1>
void Disconnect(Communicator1* receiver) {
disconnect(this, &ViewCommunicator::SendMsg, receiver, &Communicator1::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);
}
};

class MViewCommunicator : public QMainWindow, public MsgHandler {
Q_OBJECT
public:
MViewCommunicator(QWidget* parent = nullptr) :QMainWindow(parent) {
qRegisterMetaType<MsgID>("MsgID");
qRegisterMetaType<std::any>("std::any");
}
signals:
void SendMsg(MsgID, std::any);
public:
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 Communicator1>
void Connect(Communicator1* receiver) {
connect(this, &MViewCommunicator::SendMsg, receiver, &Communicator1::HandleHashMsg);
}
template<typename Communicator1>
void Disconnect(Communicator1* receiver) {
disconnect(this, &MViewCommunicator::SendMsg, receiver, &Communicator1::HandleHashMsg);
}
protected:
template<typename T, typename = std::enable_if_t<std::is_enum<T>::value, void>>
void Emit(T msg, std::any info = std::any{}) {
emit SendMsg(HashID(msg), info);
}
};

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);
}
}
补充

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


原文地址:https://blog.csdn.net/weixin_42210846/article/details/142463780

免责声明:本站文章内容转载自网络资源,如本站内容侵犯了原著者的合法权益,可联系本站删除。更多内容请关注自学内容网(zxcms.com)!