注:转自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. ROC,AUC,Precision,Recall,F1的介绍与计算

    1. 基本概念 1.1 ROC与AUC ROC曲线和AUC常被用来评价一个二值分类器(binary classifier)的优劣,ROC曲线称为受试者工作特征曲线 (receiver operatin ...

  2. Unity 代码编译成dll 更新dll实现热更代码

    Unity 代码编译成dll 更新dll实现热更代码 实现流程 代码编译成DLL DLL打包成AssetBundle 加载AssetBundle 加载代码程序集 获取指定类 使用反射赋值 C#代码编译 ...

  3. jquery中的ajax请求,阻塞ui线程的解决方案(自己总结的demo)

    /*****************************************************/ function getAjaxData(url,data){ showLoading( ...

  4. idea 没有智能提示

    开发工具要对源码产生提示,现在比较流行的是使用 .d.ts 文件 来编写提示消息 以下的解决办法不是根本之道,估计是开发工具有什么地方可以设置 自动下载 .d.ts 文件,知道的读者还请留言赐教~ 使 ...

  5. MethodImplOptions.Synchronized的一点讨论

    Review代码发现有一个方法加了[MethodImpl(MethodImplOptions.Synchronized)] 属性,这个属性的目的,从名字上就可以看出,是要对所有线程进行同步执行. 对方 ...

  6. 通过修改CR0寄存器绕过SSDT驱动保护

    为了安全起见,Windows XP及其以后的系统将一些重要的内存页设置为只读属性,这样就算有权力访问该表也不能随意对其修改,例如SSDT.IDT等.但这种方法很容易被绕过,我们只要将这些部分修改为可写 ...

  7. 基于sql service会话共享,实现SSO

    1:session的存储基于sql service数据库来存储 2:修改sql service中会话管理的系统存储过程 3:实现几个站点的会话共享 4:应用共享会话,实现单点登录

  8. 修改Hosts文件提示没有权限怎么办

    解决办法:给host文件赋予权限 1.打开电脑C盘,在目录C:\Windows\System32\drivers\etc 下找到hosts文件 2.右键hosts文件,选择属性 3.点击hosts属性 ...

  9. 前端通信:ajax设计方案(七)--- 增加请求错误监控、前端负载均衡以、请求宕机切换以及迭代问题修复

    距离上个迭代过了很长时间,中间经历了很多事情,也在每个空余时间构思了这个迭代的东西以及下个迭代要做的东西.时间周期稍微长了,望见谅. 而且,至今这个开源库的start也已经到了165个了,会支持关注和 ...

  10. css text-align文字两端对齐

    text-align:start | end | left | right | center | justify | match-parent | justify-all justify: 内容两端对 ...