注:转自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. Consul集群搭建

    一.集群搭建 准备三台机器 需要开启的端口,8300, 8301, 8500, 8600 机器1: 172.16.106.201 ./consul agent -server -bootstrap-e ...

  2. POJ 2583

    #include<iostream> #include<stdio.h> using namespace std; int main() { //freopen("a ...

  3. gvim配置相关

    用 vundle 来管理 vim 插件(包含配置文件vimrc和gvimrc) gvim插件管理神器:vundle的安装与使用 Vim插件管理Vundle Linux 下VIM的配置 Vim配置系列( ...

  4. Java8-用Lambda表达式给List集合排序

    Lambda用到了JDK8自带的一个函数式接口Comparator<T>. 准备一个Apple类 public class Apple { private int weight; priv ...

  5. 轻量级web富文本框——wangEditor使用手册(1)——基本应用 demo

    最新版wangEditor: 配置说明:http://www.wangeditor.com/doc.html demo演示:http://www.wangeditor.com/wangEditor/d ...

  6. ThreadPoolExecutor参数讲解

    1. 线程池可以节省创建多个线程带来的开销问题. 2. 线程池的参数如下: public ThreadPoolExecutor(int corePoolSize, int maximumPoolSiz ...

  7. java提供类与cglib包实现动态代理

    终于有点空余时间,决定把之前学习的知识点整理一下,备以后复习. 动态代理有三角色:抽象角色,代理角色,真是角色. 第一个记录下java提供的动态代理.即使用Proxy类和InvocationHande ...

  8. Java队列——线程池创建的例子

    线程池为线程生命周期开销问题和资源不足问题提供了解决方案.通过对多个任务重用线程,线程创建的开销被分摊到了多个任务上.其好处是,因为在请求到达时线程已经存在,所以无意中也消除了线程创建所带来的延迟.这 ...

  9. Java NIO系列教程(二) Channel

    Java NIO的通道类似流,但又有些不同: 既可以从通道中读取数据,又可以写数据到通道.但流的读写通常是单向的. 通道可以异步地读写. 通道中的数据总是要先读到一个Buffer,或者总是要从一个Bu ...

  10. 性能提速:debounce(防抖)、throttle(节流/限频)

    debounce与throttle是用户交互处理中常用到的性能提速方案,debounce用来实现防抖动,throttle用来实现节流(限频).那么这两个方法到底是什么(what)?为何要用(why-解 ...