注:转自https://www.cnblogs.com/qicosmos/archive/2013/04/28/3048919.html

应用开发过程中经常会处理对象间通信的问题,一般都是对象或接口的依赖和引用去实现对象间的通信,这在一般情况下是没问题的,但是如果相互通信的对象很多,可能会造成对象间的引用关系像蜘蛛网一样,这样会导致对象关系很复杂,难以维护的问题,解决这个问题的一个好方法是通过消息总线去解耦对象间大量相互引用的紧耦合的关系。

设计思路:被通信对象向消息总线发布一个主题,这个主题包含消息主题、消息类型和消息处理函数,消息主题标示某个特定的主题,消息类型用来区分标示这个主题会响应某个特定的消息,消息处理函数用来响应该主题的某种消息类型。通信对象向消息总线发送某个特定主和某个特定消息,总线就会根据消息主题和消息类型找到对应的消息处理函数处理该请求。

由于用到了c++11的可变模板参数和lamda表达式,windows上编译需要Compiler Nov 2012 CTP,linux需要GCC4.7以上。

具体代码:

#pragma once
#include <boost/tuple/tuple.hpp>
#include <boost/utility.hpp>
#include <boost/unordered_map.hpp>
#include <boost/any.hpp> template <typename... Args>
struct Impl; template <typename First, typename... Args>
struct Impl<First, Args...>
{
static std::string name()
{
return std::string(typeid(First).name()) + " " + Impl<Args...>::name();
}
}; template <>
struct Impl<>
{
static std::string name()
{
return "";
}
}; template <typename... Args>
std::string type_name()
{
return Impl<Args...>::name();
} class MessageBus : boost::noncopyable
{
public:
//向某个主题注册主题,需要订阅主题(topic、消息类型)和消息处理函数。
template<typename... TArgs, typename TObject, typename TMember>
void Attach(string strTopic, TObject* Object, TMember Member)
{
std::function<void(TArgs...)> f = std::function<void(TArgs...)>([=](TArgs... arg){(Object->*Member)(arg...);}); m_map.insert(make_pair(GetKey(strTopic), f));
} //向某个主题发送消息, 需要主题和消息类型。消息总线收到消息后会找到并通知对应的消息处理函数。
template<typename... Args>
void SendReq(string strTopic, Args... args)
{
auto range=m_map.equal_range(GetKey(strTopic));
boost::unordered_multimap<string, boost::any>::iterator it; for (it = range.first; it!= range.second; it++)
{
std::function<void(Args...)> f = boost::any_cast<std::function<void(Args...)>>(it->second);
f(args...);
}
} //移除某个主题, 需要主题和消息类型
template<typename... Args>
void Remove(string strTopic)
{
auto it = m_map.find(GetKey(strTopic));
while(it!=m_map.end())
m_map.erase(it++);
} private:
//获得消息键值,通过某个主题和消息类型可以确定观察者
template<typename... TArgs>
string GetKey(string& strTopic)
{
return strTopic + type_name<TArgs...>();
} private:
boost::unordered_multimap<string, boost::any> m_map;
};

测试代码:

    MessageBus bus;
MyStruct st;
bus.Attach<int,string>("bb", &st, &MyStruct::Test); //注册主题(topic、消息类型、消息处理函数)
bus.Attach<int,string>("bb", &st, &MyStruct::Test2);
bus.SendReq<int, string>("bb",0," append"); //发送消息处理请求(主题和消息类型)
bus.Remove<int, string>("bb"); //移除主题(主题和消息类型)

测试结果:

it is a test: 0 append

it is a test2: 0 append

更新版本,通过万能的函数包装器实现消息总线,使得接口的调用更加通用和一致。

template <typename R=void>
class MessageBus : boost::noncopyable
{
public:
//注册消息
template< class... Args, class F, class = typename std::enable_if<!std::is_member_function_pointer<F>::value>::type>
void Attach(string strKey, F && f)
{
std::function<R(Args...)> fn = [&](Args... args){return f(std::forward<Args>(args)...); };
m_map.insert(std::make_pair(strKey + type_name < Args...>(), std::move(fn)));
} // non-const member function
template<class... Args, class C, class... DArgs, class P>
void Attach(string strKey, R(C::*f)(DArgs...), P && p)
{
std::function<R(Args...)> fn = [&, f](Args... args){return (*p.*f)(std::forward<Args>(args)...); };
m_map.insert(std::make_pair(strKey + type_name < Args...>(), std::move(fn)));
} template<class... Args, class C, class... DArgs, class P>
void Attach(string strKey, R(C::*f)(DArgs...) const, P && p)
{
std::function<R(Args...)> fn = [&, f](Args... args){return (*p.*f)(std::forward<Args>(args)...); };
m_map.insert(std::make_pair(strKey + type_name < Args...>(), std::move(fn)));
} //广播消息,主题和参数可以确定一个消息, 所有的消息接收者都将收到并处理该消息
template<typename... Args>
void SendReq(string strTopic, Args... args)
{
auto range = m_map.equal_range(strTopic + type_name < Args...>());
for (auto it = range.first; it != range.second; it++)
{
std::function<R(Args...)> f = boost::any_cast<std::function<R(Args...)>>(it->second);
f(args...);
}
} //移除消息
template<typename... Args>
void Remove(string strTopic)
{
string strMsgType = GetNameofMsgType<Args...>();
auto range=m_map.equal_range(strTopic+strMsgType);
m_map.erase(range.first, range.second);
} private:
std::multimap<string, boost::any> m_map;
};

测试代码:

struct A
{
void Test(int x){ cout << x << endl; }
void GTest()
{
cout << "it is a test" << endl;
}
void HTest(int x) const
{
cout << "it is a HTest" << endl;
}
}; void GG(int x)
{
cout << "it is a gg" << endl;
} void GG1()
{
cout << "it is a GG" << endl;
} void TestMessageBus()
{
A a;
MessageBus<> bus;
bus.Attach<int>("aa", &A::Test, &a);
int x = 3;
bus.SendReq("aa", 3); bus.Attach<int>("hh", &A::HTest, &a);
bus.SendReq("hh", x);
bus.Attach("bb", &A::GTest, &a);
bus.SendReq("bb"); bus.Attach<int>("gg", GG);
bus.SendReq("gg", 3); bus.Attach("gg", GG1);
bus.SendReq("gg");
}

(转发)一个通用的C++ 消息总线框架的更多相关文章

  1. JavaWeb 消息总线框架 Saka V0.0.1 发布

    端午闲着无聊,自己撸了一个简单的框架,可以实现在使用SendClient发送消息,在Spring容器中,符合该消息机制的接收器将能够被执行,目前Saka处于0.0.1版本[Saka-GIthub地址( ...

  2. 第一章 搭建一个通用的.net core项目框架

    项目目标部署环境:CentOS 7+ 项目技术点:.netcore2.0 + Autofac +webAPI + NHibernate5.1 + mysql5.6 + nginx 开源地址:https ...

  3. Android组件化方案及组件消息总线modular-event实战

    背景 组件化作为Android客户端技术的一个重要分支,近年来一直是业界积极探索和实践的方向.美团内部各个Android开发团队也在尝试和实践不同的组件化方案,并且在组件化通信框架上也有很多高质量的产 ...

  4. 分享一个分布式消息总线,基于.NET Socket Tcp的发布-订阅框架,附代码下载

    一.分布式消息总线 在很多MIS项目之中都有这样的需求,需要一个及时.高效的的通知机制,即比如当使用者A完成了任务X,就需要立即告知使用者B任务X已经完成,在通常的情况下,开发人中都是在使用者B所使用 ...

  5. Android消息总线的演进之路:用LiveDataBus替代RxBus、EventBus

    背景 对于Android系统来说,消息传递是最基本的组件,每一个App内的不同页面,不同组件都在进行消息传递.消息传递既可以用于Android四大组件之间的通信,也可用于异步线程和主线程之间的通信.对 ...

  6. 物联网网关开发:基于MQTT消息总线的设计过程(上)

    道哥的第 021 篇原创 目录 一.前言 二.网关的作用 2.1 指令转发 2.2 外网通信 2.3 协议转换 2.4 设备管理 2.5 边沿计算(自动化控制) 三.网关内部进程之间的通信 3.1 网 ...

  7. dbus 消息和消息总线实例讲解-一

    应用程序A和消息总线连接,这个连接获取了一个众所周知的公共名(记作连接A).应用程序A中有对象A1提供了接口I1,接口I1有方法M1. 应用程序B和消息总线连接,要求调用连接A上对象A1的接口I1的方 ...

  8. SpringCloud Bus消息总线

    在微服务架构中,通常会使用轻量级的消息代理来构建一个共用的消息主题来连接各个微服务实例,它广播的消息会被所有在注册中心的微服务实例监听和消费,也称消息总线. SpringCloud中也有对应的解决方案 ...

  9. 消息总线VS消息队列

    前段时间实现了一个基于RabbitMQ的消息总线,实现的过程中自己也在不断得思考.总结以及修正.需要考虑各个维度:效率.性能.网络.吞吐量.甚至需要自己去设想API可能的使用场景.模式.不过能有一件事 ...

随机推荐

  1. 【javascript】您好, 您要的ECMAScript6速记套餐到了 (一)

    [前言]本文“严重参考” 自阮一峰老师写的ES6文档,在此我郑重感谢他沉默无声的帮助 总结一下ES6为 javascript中的 对象/数组/函数 这JS三巨头所提供的更简洁优雅的书写方式,以及扩展的 ...

  2. Dubbo实现原理之基于SPI思想实现Dubbo内核

    dubbo中SPI接口的定义如下: @Documented @Retention(RetentionPolicy.RUNTIME) @Target({ElementType.TYPE}) public ...

  3. Swift5 语言参考(七) 属性

    属性提供有关声明或类型的更多信息.Swift中有两种属性,即适用于声明的属性和适用于类型的属性. 您可以通过编写@符号后跟属性的名称以及属性接受的任何参数来指定属性: @attribute name ...

  4. git log 高级用法

    转自:https://github.com/geeeeeeeeek/git-recipes/wiki/5.3-Git-log%E9%AB%98%E7%BA%A7%E7%94%A8%E6%B3%95 内 ...

  5. MySQL 主从复制相关参数

    列举了MySQL主从复制主要的相关参数 binlog server_id 服务器在集群中唯一标识符 log_bin[=binlog_name] 启动二进制日志 log_bin_index 二进制日志索 ...

  6. ThinkPHP5代码执行的简单分析

    漏洞影响版本: ThinkPHP 5.0.5-5.0.22 ThinkPHP 5.1.0-5.1.30 漏洞复现: 一.mac的debug环境搭建. 一键化环境搭建工具: mamp pro ,调试工具 ...

  7. AndroidStudio配置LitePal

    配置,许多书上还有教程都忽略了将LitePal下载下来和拷贝的过程,这里写一个详细的课程 首先,前往GitHub,下载LitePal的包. 然后解压,会看到这个 进入download 自己选个版本,然 ...

  8. 阿里云负载不支持 WebSocket 协议与 WSS 和 Nginx 配置问题

    WebSocket 是 HTML5 下一种新的协议.它实现了浏览器与服务器全双工通信,能更好的节省服务器资源和带宽并达到实时通讯的目的.它与HTTP一样通过已建立的TCP连接来传输数据,但是它和HTT ...

  9. 安装的Android SDK下无doc文件夹问题 以及关联Android帮助文档和查看文档 以及查看在线文档

    参考连接:https://blog.csdn.net/fangzicheng/article/details/78344521 https://jingyan.baidu.com/article/29 ...

  10. github绑定自己的域名

    1.进入需要绑定域名的项目里面,然后新建一个文件CNAME,没有后缀的. 2.在文件里面输入你的域名,然后保存.github这边就完成了. 3.然后去你购买域名的网站,进入控制台,找到域名,然后域名解 ...