一、概述
本实现参考自一个通用的C++ 消息总线框架,代码位置:messagebus.h。
该消息总线主要解决多个模块间的耦合问题,是阻塞式的。其基本原理如下:
- 通信对象向消息总线发布一个主题,包含消息主题和消息处理函数。
- 消息主题标示特定主题,消息处理函数用来响应该主题的某种消息类型。
- 通信对象向消息总线发送特定主题和消息参数,总线根据消息主题和消息参数找到对应的消息处理函数来处理请求。
关键成员对象:static std::multimap<std::string, std::any> m_map;
其它语言版本的实现:
二、实现代码
不含Boost的版本(使用C++17标准库)
#pragma once
#include <functional>
#include <map>
#include <string>
#include <any>
// 简单的消息分发机制
class MessageBus {
public:
// 注册非成员函数或静态成员函数
template <class... Args, class Func,
class = std::enable_if_t<!std::is_member_function_pointer_v<Func>>>
void Attach(const std::string& key, Func&& func) {
std::function<void(Args...)> fn = std::forward<Func>(func);
m_map_.emplace(key, std::move(fn));
}
// 注册非常量成员函数
template <class... Args, class Class, class... DeclaredArgs, class Object>
void Attach(const std::string& key, void (Class::*func)(DeclaredArgs...), Object& object) {
std::function<void(Args...)> fn = [&object, func](Args... args) {
(object.*func)(std::forward<Args>(args)...);
};
m_map_.emplace(key, std::move(fn));
}
// 注册常量成员函数
template <class... Args, class Class, class... DeclaredArgs, class Object>
void Attach(const std::string& key, void (Class::*func)(DeclaredArgs...) const, Object& object) {
std::function<void(Args...)> fn = [&object, func](Args... args) {
(object.*func)(std::forward<Args>(args)...);
};
m_map_.emplace(key, std::move(fn));
}
// 广播消息,调用所有回调函数
template <typename... Args>
void SendMessage(const std::string& key, Args... args) {
auto range = m_map_.equal_range(key);
for (auto it = range.first; it != range.second; ++it) {
std::any_cast<std::function<void(Args...)>>(it->second)(std::forward<Args>(args)...);
}
}
// 移除指定key的所有回调函数
void Remove(const std::string& key) {
m_map_.erase(key);
}
private:
static std::multimap<std::string, std::any> m_map_;
};
std::multimap<std::string, std::any> MessageBus::m_map_;
static MessageBus g_messagebus; // 全局消息总线
含有Boost的版本
#pragma once
#include <functional>
#include <map>
#include <string>
#include <boost/any.hpp>
// 简单的消息分发机制
class MessageBus {
public:
// 注册非成员函数或静态成员函数
template <class... Args, class Func,
class = std::enable_if_t<!std::is_member_function_pointer_v<Func>>>
void Attach(const std::string& key, Func&& func) {
std::function<void(Args...)> fn = std::forward<Func>(func);
m_map_.emplace(key, std::move(fn));
}
// 注册非常量成员函数
template <class... Args, class Class, class... DeclaredArgs, class Object>
void Attach(const std::string& key, void (Class::*func)(DeclaredArgs...), Object& object) {
std::function<void(Args...)> fn = [&object, func](Args... args) {
(object.*func)(std::forward<Args>(args)...);
};
m_map_.emplace(key, std::move(fn));
}
// 注册常量成员函数
template <class... Args, class Class, class... DeclaredArgs, class Object>
void Attach(const std::string& key, void (Class::*func)(DeclaredArgs...) const, Object& object) {
std::function<void(Args...)> fn = [&object, func](Args... args) {
(object.*func)(std::forward<Args>(args)...);
};
m_map_.emplace(key, std::move(fn));
}
// 广播消息,调用所有回调函数
template <typename... Args>
void SendMessage(const std::string& key, Args... args) {
auto range = m_map_.equal_range(key);
for (auto it = range.first; it != range.second; ++it) {
boost::any_cast<std::function<void(Args...)>>(it->second)(std::forward<Args>(args)...);
}
}
// 移除指定key的所有回调函数
void Remove(const std::string& key) {
m_map_.erase(key);
}
private:
static std::multimap<std::string, boost::any> m_map_;
};
std::multimap<std::string, boost::any> MessageBus::m_map_;
static MessageBus g_messagebus; // 全局消息总线
三、使用方法
在任意一个cpp文件中实现m_map
,例如在main函数中:
#include "messagebus.h"
#include <iostream>
std::multimap<std::string, std::any> MessageBus::m_map_;
// 使用boost时:std::multimap<std::string, boost::any> MessageBus::m_map_;
class Test {
public:
void TestFunc(int, const char*) {
std::cout << "hit test" << std::endl;
}
};
int main() {
Test t;
std::string key = "test";
// 订阅"test"消息,回调函数为Test::TestFunc
g_messagebus.Attach<int, const char*>(key, &Test::TestFunc, t);
// 向订阅者发送消息,消息参数是123和"abc",回调函数Test::TestFunc将会被执行
g_messagebus.SendMessage<int, const char*>(key, 123, "abc");
// 移除订阅"test"
g_messagebus.Remove(key);
return 0;
}
四、非阻塞消息总线的实现
请参考我的另一篇博客: 使用C++11实现的非阻塞消息总线message_bus