注:转自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. Linux基础命令-cd

    cd 作用:切换路径 切换至家目录 $ cd $ cd~ 在上一个目录和当前目录来回切换 $ cd - 切换至某用户的家目录 # cd ~ # pwd /root # cd ~quail #pwd / ...

  2. Apache-通过CGI执行脚本

    1.配置服务器,开启注释 vim /etc/httpd/conf/httpd.conf 292 # (You will also need to add "ExecCGI" to ...

  3. padding(内边框), border(边框), margin, 标准文档流, 块级元素和行内元素, 浮动 ,margin的用法

    盒模型 在CSS中,"box model"这一术语是用来设计和布局时使用,然后在网页中基本上都会显示一些方方正正的盒子.我们称为这种盒子叫盒模型. 盒模型有两种:标准模型和IE模型 ...

  4. git add .添加不成功

    情景: 我首先在一个有许多文件的文件夹中  git init  创建一个git管理仓库 之后 git add . 之后 git commit -m "提交" 发现提交不成功,文件没 ...

  5. 转---谈谈HTTP协议中的短轮询、长轮询、长连接和短连接

    作者:伯乐在线专栏作者 - 左潇龙 http://web.jobbole.com/85541/ 如有好文章投稿,请点击 → 这里了解详情 引言 最近刚到公司不到一个月,正处于熟悉项目和源码的阶段,因此 ...

  6. [原创]内网渗透JSP webSehll连接工具

    工具: JspShellExec编译: VS2012  C# (.NET Framework v2.0)组织: K8搞基大队[K8team]作者: K8拉登哥哥博客: http://qqhack8.b ...

  7. 05-01 Java 方法

    方法 方法定义.格式: /* 方法:完成特定功能的代码块. 注意:在很多语言里面有函数的定义,而在Java中函数被称为方法. 方法格式: 修饰符 返回值类型 方法名(参数类型 参数名1,参数类型 参数 ...

  8. 课程一(Neural Networks and Deep Learning),第二周(Basics of Neural Network programming)—— 3、Python Basics with numpy (optional)

    Python Basics with numpy (optional)Welcome to your first (Optional) programming exercise of the deep ...

  9. mysql基础知识(3)

    十六.组合查询 使用 union 来组合查询,如果第一个查询返回M行,第二个查询返回N行,那么组合查询的结果一般为 M+N 行. 注意:每个查询必须包含相同的行.表达式的聚集函数:默认会去除相同行.表 ...

  10. Linux下升级Python到3.5.2版本

    原文出处:https://www.cnblogs.com/tssc/p/7762998.html 本文主要介绍在Linux(CentOS)下将Python的版本升级为3.5.2的方法 众所周知,在20 ...