C++11的消息总线

一、什么是消息总线

在大规模的软件开发过程中,对象很多,关联关系非常复杂,如果没有统一的、简洁的方法去管理这些对象关系,会导致对象之间交互困难,后期难以维护。

基于消息总线技术可以解决这些问题。在消息总线中,对象是通过消息来联系,消息即是对象的关系。只需要在消息总线中管理这些消息,不用关心具体哪些对象之间有关联,便于统一管理。

二、消息总线包含的内容

2.1 消息定义

定义一种通用的消息格式,让所有的对象都能接收。

通用的消息类型的定义:主题+泛型函数的签名。主题用来将消息的接收者进行分组,只有某个特定组的接收者才能收到消息。泛型函数用来确定哪些接收者具备接收该消息的能力。

这里主题用string类型来表示,

泛型函数可能是所有的可调用对象,例如:普通函数,成员函数,函数对象,std::function和lamda表达式。要统一这些接口,就是将这些全部转换为std::function。

std::function<R(Args…)>

std::function作为消息类型
R 函数返回类型
Args… 是一个可变参数模板,代表了任意个数类型的参数

2.2 消息的注册

某个对象注册以后,当需要使用的时候,会告诉总线,总线就会遍历该消息列表容器,查找对应的消息和消息的接收者,找到以后再广播消息。

消息类型:topic + std::function<R(Args...)>。

对于不同的Args和R,消息类型是不同的。为了保存不同的消息对象,需要用Any做类型擦除。

最后,消息总线内部使用multimap来保存消息,键为topic + typeid的字符串,值为消息对象。

std::multimap<string,Any> m_map;

消息注册如下:

// 注册可调用对象
template<typename T>
void Attach(const string& strTopic, const F& f)
{
	auto func = to_function(std::forward<F>(f));
    Add(strTopic, std::move(func));
}
 
template<typename T>
void Add(const string& strTopic, F&& f)
{
    // typeid 运算符用来获取一个表达式的类型信息
    string strKey = strTopic + typeid(F).name();
    // Any类型擦除
    m_map.emplace(std::move(strMsgType), std::forward<F>(f));
}

 

2.3 消息的分发

消息总线通过主题发送消息。会查找消息总线内部的容器,找到对这个消息感兴趣的对象,并一一调用,即一一发送消息。

template<typename R, typename... Args>
void sendReq(Args&&... args, const string& strTopic = "")
{
    using function_type = std::function<R(Args...)>;                
    string strMsgType = strTopic + typeid(function_type).name();    
    auto range = m_map.equal_range(strMsgType);                     
    for (Iterator it = range.first(); it != range.second; it++)     
    {
        auto f = it->second.AnyCast<function_type>();               
        f(std::forward<Args>(args)...);                             
    }
}

三、消息总线的实现

设计思路

融合了观察者模式和中介者模式,通过Any类擦除了消息的类型,使得消息总线可以管理所有类型的消息。观察者模式用来维护主题和在适当的时候向观察者广播消息;中介者模式主要用来降低观察者模式相互依赖产生的耦合性,使各对象不需要显式地相互引用,而且可以独立地改变它们之间的交互关系。

    NonCopyable类 :防止类的拷贝,需要这个功能的都可以继承它

    MessageBus类: 消息总线

    Any类:存储任何对象的容器

    function_traits:获取普通函数、函数指针、std::function、函数对象以及成员函数的函数类型、返回类型、参数个数和参数的具体类型

文件目录结构如下

 NonCopyable.h

/*
 * THIS CLASS CAN DEFINE A CALSS WHICH NOT SUPPORT COPY CONSTRUTOR
 * AND COPY OPERATOR FUNCTION
 */

class NonCopyable
{
	protected:
		NonCopyable() = default;
		~NonCopyable() = default;
		NonCopyable(const NonCopyable&) = delete;
		NonCopyable& operator=(const NonCopyable&) = delete;
};

Any.h

#include <memory>
#include <iostream>
#include <typeindex>

struct Any
{
	Any(void) : m_tpIndex(std::type_index(typeid(void))) {}
	//复制拷贝构造函数
	Any(Any &that) : m_ptr(that.Clone()), m_tpIndex(that.m_tpIndex) {}
	Any(const Any &that) : m_ptr(that.Clone()), m_tpIndex(that.m_tpIndex) {}
	Any(Any &&that) : m_ptr(std::move(that.m_ptr)), m_tpIndex(that.m_tpIndex) {}

 	//std::enable_if  条件判断,只有当条件满足时,才有效,否则编译报错
    //std::is_same  判断两个类型是否相同
    //std::decay  擦除类型的修饰,退化为基本形态
	template <typename U, class = 
		typename std::enable_if<!std::is_same<typename std::decay<U>::type, Any>::value, U>::type>
		Any(U &&value) : m_ptr(new Derived<typename std::decay<U>::type>(std::forward<U>(value))),
		m_tpIndex(std::type_index(typeid(typename std::decay<U>::type))) {}

	bool IsNULL() const { return !bool(m_ptr); }

	template <class U>
	bool Is() const // //判断给定的类型与存储的数据类型是否相同
	{
		return m_tpIndex == std::type_index(typeid(U));
	}

	template <class U>
	U &AnyCast()
	{
		if (!Is<U>())
		{
			std::cout << "can not cast " << typeid(U).name() << " to " << m_tpIndex.name() << std::endl;
		}

		auto derived = dynamic_cast<Derived<U> *>(m_ptr.get());
		return derived->m_value;
	}

	Any &operator=(const Any &a)
	{
		if (m_ptr == a.m_ptr)
			return *this;

		m_ptr = a.Clone();
		m_tpIndex = a.m_tpIndex;
		return *this;
	}

private:
	struct Base; // 存放原始数据的基类
	typedef std::unique_ptr<Base> BasePtr;
	struct Base
	{
		virtual ~Base() {}
		virtual BasePtr Clone() const = 0;
	};

	template <typename T>
	struct Derived : Base
	{
		template <typename U>
		Derived(U &&value) : m_value(std::forward<U>(value)) {}

		BasePtr Clone() const
		{
			return BasePtr(new Derived<T>(m_value));
		}

		T m_value;
	};

public:
	BasePtr Clone() const
	{
		if (m_ptr != nullptr)
			return m_ptr->Clone();
		return nullptr;
	}

	BasePtr m_ptr;
	std::type_index m_tpIndex;
};

function_traits.h

#include <functional>
#include <tuple>

//转换为std::function和函数指针. 
template<typename T>
struct function_traits;

// 普通函数
template<typename Ret, typename... Args>
struct function_traits<Ret(Args...)>
{
	public:
		enum {arity = sizeof...(Args)};
		typedef Ret function_type(Args...);
		typedef Ret return_type;
		using stl_function_type = std::function<function_type>; // 自定义函数类型
		typedef Ret(*pointer)(Args...);

		template<size_t I>
		struct args
		{
			static_assert(I < arity, "index is out of range, index must less than sizeof Args");
			using type = typename std::tuple_element<I, std::tuple<Args...>>::type;
		};
};

// 函数指针
template<typename Ret, typename... Args>
struct function_traits<Ret(*)(Args...)> : function_traits<Ret(Args...)> {};

//std::function
template<typename Ret, typename... Args>
struct function_traits<std::function<Ret(Args...)>> : function_traits<Ret(Args...)>{};

// 成员函数
#define FUNCTION_TRAITS(...) \
	template<typename ReturnType, typename ClassType, typename... Args>\
	struct function_traits<ReturnType(ClassType::*)(Args...) __VA_ARGS__> : \
		function_traits<ReturnType(Args...)>{};

FUNCTION_TRAITS();
FUNCTION_TRAITS(const);
FUNCTION_TRAITS(volatile);
FUNCTION_TRAITS(const volatile);

// 函数对象
template<typename Callable>
struct function_traits : function_traits<decltype(&Callable::operator())>{};

// 左值引用的lamda表达式
template<typename Function>
struct function_traits<Function>::stl_function_type to_function(const Function &lambda)
{
	return static_cast<typename function_traits<Function>::stl_function_type>(lambda);
}

//  右值引用的lamda表达式
template<typename Function>
struct function_traits<Function>::stl_function_type to_function(Function && lambda)
{
	return static_cast<typename function_traits<Function>::stl_function_type>(std::forward<Function>(lambda));
}

// 指针对象
template<typename Function>
struct function_traits<Function>::pointer to_function_pointer(const Function &lambda)
{
	return static_cast<typename function_traits<Function>::pointer>(lambda);
}

MessageBus.h

#include <string>
#include <functional>
#include <map>
#include "Any.h"
#include "function_traits.h"
#include "NonCopyable.h"

using namespace std;

class MessageBus : NonCopyable
{
	public:
		///注册消息
		template<typename F>
		void Attach(F && f, const string& strTopic="")
		{
			auto func = to_function(std::forward<F>(f));
			Add(strTopic, std::move(func));
		}

		//发送消息
		template<typename R>
		void SendReq(const string &strTopic="")
		{
			using function_type = std::function<R()>;
			string strMsgType = strTopic + typeid(function_type).name();
			auto range = m_map.equal_range(strMsgType);  //range  std::pair<iterator, iterator>
			//std::pair<std::multimap<string, Any>::iterator, std::multimap<string, Any>::iterator> range = m_map.equal_range(strMsgType);
			for(Iterater it=range.first; it!=range.second; ++it)
			{
				auto f = it->second.AnyCast<function_type>();
				f();
			}
		}

		template<typename R, typename... Args>
		void SendReq(Args&&... args, const string& strTopic="")
		{
			// std::function  函数对象类,包装了满足该函数模板类型的任意函数对象,
			// 这些目标实体包括普通函数、Lambda表达式、函数指针、模函数、类成员函数、
			// 类静态函数以及其它的函数对象
			using function_type = std::function<R(Args...)>;
			string strMsgType = strTopic + typeid(function_type).name();
			auto range = m_map.equal_range(strMsgType);
			for(Iterater it=range.first; it!=range.second; ++it)
			{
				auto f = it->second.AnyCast<function_type>();
				f(std::forward<Args>(args)...);
			}
		}

		// 移除某个主题,需要主题和消息类型
		template<typename R, typename... Args>
		void Remove(const string& strTopic="")
		{
			using function_type = std::function<R(Args...)>;

			string strMsgType = strTopic + typeid(function_type).name();
			int count = m_map.count(strMsgType);
			auto range = m_map.equal_range(strMsgType);
			m_map.erase(range.first, range.second);
		}

	private:
		template<typename F>
		void Add(const string& strTopic, F && f)
		{
			string strMsgType = strTopic + typeid(F).name();
			m_map.emplace(std::move(strMsgType), std::forward<F>(f));
		}

	private:
		std::multimap<string, Any> m_map;
		typedef std::multimap<std::string, Any>::iterator Iterater;

};

test.cpp

#include "MessageBus.h"
#include <iostream>

MessageBus g_bus; // 全局的消息总线
const string Topic = "Drive"; // 主题
const string CallBackTopic = "DriveOK"; //执行结果返回主题

struct Subject
{
	Subject()
	{
		g_bus.Attach([this] { DriveOk(); }, CallBackTopic);
	}

	void SendReq(const string &topic)
	{
		g_bus.SendReq<void, int>(50, topic);
	}

	void DriveOk()
	{
		std::cout << "DriveOk" << std::endl;
	}
};

struct Car
{
	Car()
	{
		g_bus.Attach([this](int speed){Drive(speed);}, Topic);
	}

	void Drive(int speed)
	{
		std::cout << "Car Drive.." << std::endl;
		g_bus.SendReq<void>(CallBackTopic);
	}
};

struct Bus
{
	Bus()
	{
		g_bus.Attach([this](int speed){Drive(speed);}, Topic);
	}

	void Drive(int speed)
	{
		std::cout << "Bus Drive..." << std::endl;
		g_bus.SendReq<void>(CallBackTopic);
	}
};


struct Truck
{
	Truck()
	{
		g_bus.Attach([this](int speed){Drive(speed);});
	}

	void Drive(int speed)
	{
		std::cout << "Truck Drive.." << std::endl;
		g_bus.SendReq<void>(CallBackTopic);
	}
};


int main()
{
	Subject subject;
	Car car;
	Bus bus;
	Truck truck;
	subject.SendReq(Topic);
	std::cout << "*****" << std::endl;
	subject.SendReq("");
	return 0;
}

编译

g++ test.cpp -o test -std=c++11
g++ TestMessageBus.cpp -o TestMessageBus -std=c++11

运行

 github链接

https://github.com/hejiajie1989/MessageBus

参考:

c++11实现的一个消息总线框架_hejiajie1989的博客-CSDN博客_c++ 消息总线

[12 使用C++11开发一个对象的消息总线(观察者模式和中介者模式的应用及优化)] 12.2 消息总线关键技术_站在巨人的肩膀上,温故而知新的博客-CSDN博客

  • 1
    点赞
  • 13
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值