描述
基于QT信号槽机制实现类之间的交互调用通信,适用于使用不同枚举作为消息交互的类型场景,支持附带任意参数,代码使用方式参考链接
特性
- 代码简洁,不超过100行
- 仅需包含一个头文件Communicator.h,需要通信的业务QT类使用宏
Q_ENUM_SIGSLOT
即可使用枚举信号槽 - 支持消息总线,可定义模块进行模块化通信,模块标识使用枚举即可
- 消息通过枚举类型定义,支持任意枚举消息,添加枚举无需改动代码。枚举名称区分消息类型,枚举值区分具体消息
- 使用便捷,只需注册消息和消息处理函数
- 提供发送消息时,附带any传递任意额外信息
- 支持多线程,同QT一致的信号槽触发方式,如需控制信号槽触发方式,需要额外补充代码
对比QT信号槽
- 适用于使用不同枚举作为消息类型场景,支持附带任意参数
- 使用同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结合起来用就不太适宜了。