C++的阻塞式消息总线message_bus实现_c++ messagebus_橘色的喵的博客-程序员秘密

技术标签: messagebus  c++  阻塞式  C++11  消息总线  

一、概述

  • 本实现参考自一个通用的C++ 消息总线框架 ,代码位置:messagebus.h
  • 该消息总线主要是解决多个模块间的耦合问题,是阻塞式的。
  • 消息总线实现的基本原理如下:被通信对象向消息总线发布一个主题,这个主题包含消息主题和消息处理函数,消息主题标示某个特定的主题,消息处理函数用来响应该主题的某种消息类型。通信对象向消息总线发送某个特定主题和消息参数,总线就会根据消息主题和消息参数找到对应的消息处理函数处理该请求。
  • 最主要的是这个成员对象boost::unordered_multimap<string, boost::any> m_map;
    其它语言版本实现:
  • lua语言的消息总线的实现:lua简单消息总线实现,类似于C++ Qt的信号槽
  • python语言的消息总线的实现:python简单消息总线实现,类似于C++ Qt的信号槽

二、实现代码

  • messagebus.h ,需使用C++17,不含有boost的版本,用到了std::any。
#pragma once

#include <functional>
#include <map>
#include <string>
#include <iostream>
#include <any>

// 简单的消息分发机制
// ref:https://www.cnblogs.com/qicosmos/archive/2013/04/28/3048919.html
// 《深入应用C++11代码优化与工程级应用》第12章
class MessageBus
{
    
public:
    // regist message
    template< class... _Args, class _Func, class = typename std::enable_if<!std::is_member_function_pointer<_Func>::value>::type>
    void attach(std::string key, _Func && func)
    {
    
        std::function<void(_Args...)> fn = [&](_Args... args) {
     return func(std::forward<_Args>(args)...); };
        m_map.insert(std::make_pair(key, std::move(fn)));
    }

    // non-const member function
    template<class... _Args, class _Class, class... _DeclareArgs, class _Object>
    void attach(std::string key, void(_Class::*func)(_DeclareArgs...), _Object & object)
    {
    
        std::function<void(_Args...)> fn = [&, func](_Args... args) {
     return (object.*func)(std::forward<_Args>(args)...); };
        m_map.insert(std::make_pair(key, fn));
    }

    // const member function
    template<class... _Args, class _Class, class... _DeclareArgs, class _Object>
    void attach(std::string key, void(_Class::*func)(_DeclareArgs...) const, _Object & object)
    {
    
        std::function<void(_Args...)> fn = [&, func](_Args... args) {
     return (object.*func)(std::forward<_Args>(args)...); };
        m_map.insert(std::make_pair(key, fn));
    }

    // Broadcast messages, call all the callback functions
    template<typename... _Args>
    void sendMessage(std::string key, _Args... args)
    {
    
        auto range = m_map.equal_range(key);
        for (auto it = range.first; it != range.second; it++)
        {
    
            std::function<void(_Args...)> func = std::any_cast<std::function<void(_Args...)>>(it->second);
            func(args...);
        }
    }

    // remove message
    template<typename... _Args>
    void remove(std::string key)
    {
    
        auto range = m_map.equal_range(key);
        m_map.erase(range.first, range.second);
    }

public:
    MessageBus() = default;
    ~MessageBus() = default;
private:
    MessageBus(const MessageBus&) = delete;
    MessageBus& operator=(const MessageBus&) = delete;

    static std::multimap<std::string, std::any> m_map;
};

static MessageBus g_messagebus; // 全局消息总线
  • messagebus.h ,含有boost的版本
#pragma once

#include <functional>
#include <map>
#include <string>
#include <iostream>

#include <boost/any.hpp>
#include <boost/lexical_cast.hpp>

// 简单的消息分发机制
// ref:https://www.cnblogs.com/qicosmos/archive/2013/04/28/3048919.html
// 《深入应用C++11代码优化与工程级应用》第12章
class MessageBus
{
    
public:
    // regist message
    template< class... _Args, class _Func, class = typename std::enable_if<!std::is_member_function_pointer<_Func>::value>::type>
    void attach(std::string key, _Func && func)
    {
    
        std::function<void(_Args...)> fn = [&](_Args... args) {
     return func(std::forward<_Args>(args)...); };
        m_map.insert(std::make_pair(key, std::move(fn)));
    }

    // non-const member function
    template<class... _Args, class _Class, class... _DeclareArgs, class _Object>
    void attach(std::string key, void(_Class::*func)(_DeclareArgs...), _Object & object)
    {
    
        std::function<void(_Args...)> fn = [&, func](_Args... args) {
     return (object.*func)(std::forward<_Args>(args)...); };
        m_map.insert(std::make_pair(key, fn));
    }

    // const member function
    template<class... _Args, class _Class, class... _DeclareArgs, class _Object>
    void attach(std::string key, void(_Class::*func)(_DeclareArgs...) const, _Object & object)
    {
    
        std::function<void(_Args...)> fn = [&, func](_Args... args) {
     return (object.*func)(std::forward<_Args>(args)...); };
        m_map.insert(std::make_pair(key, fn));
    }

    // Broadcast messages, call all the callback functions
    template<typename... _Args>
    void sendMessage(std::string key, _Args... args)
    {
    
        auto range = m_map.equal_range(key);
        for (auto it = range.first; it != range.second; it++)
        {
    
            std::function<void(_Args...)> func = boost::any_cast<std::function<void(_Args...)>>(it->second);
            func(args...);
        }
    }

    // remove message
    template<typename... _Args>
    void remove(std::string key)
    {
    
        auto range = m_map.equal_range(key);
        m_map.erase(range.first, range.second);
    }

public:
    MessageBus() = default;
    ~MessageBus() = default;
private:
    MessageBus(const MessageBus&) = delete;
    MessageBus& operator=(const MessageBus&) = delete;

    static std::multimap<std::string, boost::any> m_map;
};

static MessageBus g_messagebus; // 全局消息总线

三、使用方法

  • 在任意一cpp文件中实现m_map,比如main函数中;
  • 使用全局的g_messagebus在所需订阅模块中订阅感兴趣消息和回调函数;
  • 在需要发布消息的代码中填写sendMessage业务逻辑。
#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 test(int, const char*) {
     
		std::cout<< "hit test" << std::endl; 
		return;
	}
};
int main()
{
    
    Test t;
	std::string key = "test";
    // 订阅"test"消息,回调函数为Test::test
    g_messagebus.attach<int, const char*>(key, &Test::test, t); 
    // 向订阅者发送消息,消息参数是 123 和 "abc",回调函数Test::test将会被执行
    g_messagebus.sendMessage<int, const char*>(key, 123, "abc"); 
    // 移除订阅"test"
    g_messagebus.remove<int, const char*>(key); 
    return 0;
}
   

四、非阻塞消息总线的实现

+请参考我的另一篇博客 : 使用C++11实现的非阻塞消息总线message_bus

版权声明:本文为博主原创文章,遵循 CC 4.0 BY-SA 版权协议,转载请附上原文出处链接和本声明。
本文链接:https://blog.csdn.net/stallion5632/article/details/126096849

智能推荐

TensorFlow环境安装配置1.2.1(含anaconda下载与安装)_安装tensorflow1.2.1_爱吃绿豆糕的博客-程序员秘密

1.打开网址,清华大学开源软件镜像站:https://mirrors.tuna.tsinghua.edu.cn/2.找到对应版本点击即下载,在这里我下载的是4.2.0的64位版本(我是win10系统)3.下载后按指引安装即可4.安装完成后,可以在开始菜单看到对应选项,我们接着需要在Anaconda中安装TensorFlow,为了避免从国外下载速度过慢的问题,我们先在Anaconda修改国内镜像源:①首先,打开Anaconda Prompt窗口,执行命令:conda config --add

CronTrigger Tutorial——石英调度!_csdnfanguyinheng的博客-程序员秘密

CronTrigger TutorialIntroductioncron is a UNIX tool that has been around for a long time, soits scheduling capabilities are powerful and proven. TheCronTrigger class is based on the scheduling c

完美解决安装系统出现问题——安装程序无法创建新的系统分区,也无法定位现有的系统分区,详情请查看日志文件,_执行完毕后未在目标分区内发现系统文件_wsasy12345的博客-程序员秘密

&quot;安装程序无法创建新的系统分区&quot; 怎么办? XP换Win7系统问题1 步骤一:利用PE系统里的虚拟光驱软件加载WIN7 ISO文件。 PS: 一般PE系统都集成了该类软件的。 2 步骤二:将WIN7系统中的三个文件(夹)【boot、sources、bootmgr】拷贝到C盘根目录。 PS:先格式化C盘,再拷贝系统文件到C盘 3 步骤三:开始菜单中运行&quot;c:\boot\boots...

iview render函数使用场景_iview rener里面是干啥的_Marin6的博客-程序员秘密

一、table中序号、表格渲染//身份证号表头 sfzhtabHead: [ { // type: 'index', width: 60, align: "left", title: "序号", fixed: "left", render: (h, params) =&gt; { return h("span", params.ind.

半虚拟化和全虚拟化的区别_全虚拟化和半虚拟化区别_平凡的人的博客-程序员秘密

全虚拟化(Full virtualization), 也称为原始虚拟化技术, 是另一种虚拟化方法. 该模型使用虚拟机协调客户操作系统和原始硬件(见图2). 这里"协调"是一个关键词, 因为VMM在客户操作系统和裸硬件之间用于工作协调. 一些受保护的指令必须由Hypervisor(虚拟机管理程序)来捕获和处理. 因为操作系统是通过Hypervisor来分享底层硬件.  图2. 全虚拟

SoC嵌入式软件架构设计之五:可执行程序的重构_吴跃前的博客-程序员秘密

本节讲述代码分块(Bank)管理思想下可执行文件的重构,即对程序编译后的可执行文件进行重新组织、打包,以在加载阶段获得最高的执行效率,减少内存占用。要使执行效率高,意味着可执行文件的格式尽可能简单,解析执行文件的流程简单,相应地,解析过程代码量少,即能够减少内存的占用。

随便推点

2021年五面蚂蚁,2020还有必要学JSP吗?_m0_60388117的博客-程序员秘密

out.write("&lt;html&gt;\r\n"); out.write("&lt;head&gt;\r\n"); out.write(" &lt;title&gt;简单使用JSP&lt;/title&gt;\r\n"); out.write("&lt;/head&gt;\r\n"); out.write("&lt;body&gt;\r\n");String s = "HelloWorda";out.println(s); out.write("\r\n");...

idea提示cannot download sources 解决办法_康康滴泡沫的博客-程序员秘密

经原文博主同意转到该处记录:https://www.cnblogs.com/wwjj4811/p/10364580.html(一)(适用于单体架构)当我们点击Download Sources时:有时候idea会出现cannot download sources的情况,如下图解决办法如下:打开idea右下角的terminal在里面输入mvn dependency:resolve -Dclassifier=sources如下图所示:然后回车稍等片刻:看到..

内存管理之:STL中的内存分配器_空闲列表(free list)的分配策略_风云来的博客-程序员秘密

题记:内存管理一直是C/C++程序的红灯区。关于内存管理的话题,大致有两类侧重点,一类是内存的正确使用,例如C++中new和delete应该成对出现,用RAII技巧管理内存资源,auto_ptr等方面,很多C/C++书籍中都使用技巧的介绍。另一类是内存管理的实现,如linux内核的slab分配器,STL中的allocator实现,以及一些特定于某种对象的内存管理等。最近阅读了一些内存管理实现方面的

I - No more tricks, Mr Nanguo 佩尔方程的求解。_运行什么,直接交的博客-程序员秘密

题意:滥竽充数的故事,本来是x^2的方形矩阵有一个人滥竽充数,但是换了国王之后他要把这些人分成d份,每一份也组成一个正方形(此时那个滥竽充数的就只能走掉了),然后输入d,让你求解原来的满足条件的第k个数就多少(就是正方形的边长);x^2=d*y^2+1毕竟是刚学的新知识,虽然是入门数论,还是单独写一个吧,佩尔方程:x^2-dy^2=1。 这个可以求解很多有趣的不定方差。先看一下推导...

Redhat 5.4 Orcle RAC 数据库 从10.2.0.1升级到 10.2.0.4_小宝老豆的博客-程序员秘密

之前安装的是两个节点的RAC 平台。 数据库版本是10.2.0.1. 这个实验的目的就是将这个数据库版本从10.2.0.1 升级到 10.2.0.4.  升级包可以从Oracle metalink上进行下载,这个下载需要Oracle 付费的帐号。 网络可能也有资源下载。 10.2.0.4的patch number 是:p6810189。        两个节点的RAC 安装,参考Blog:

C语言实验报告免费阅读,《C语言实验报告合集》.doc_珍妮赵的博客-程序员秘密

《C语言程序设计》实验报告专业: 班级: 学号: 学生姓名:实验日期: 成绩: 指导老师:实验单元一 程序基本结构设计实验题目实验一 熟悉VC++环境实验目的1. 熟悉C程序编辑环境,掌握主要菜单项的作用。2. 熟悉编写一个C程序的上机过程(编辑、编译、链接和运行)。3. 熟悉C程序的基...