消息总线是一种降低耦合的一种技术,对象间只通过消息练习,不通过依赖或者关联。将复杂对象关系简化,降低复杂度,提高程序的维护性。
消息总线是一种解耦合的程序的设计方式,可以让对象之间的依赖和继承用消息注册来联系。
消息总线的关键技术
1、通用消息的定义
本质其实是让所有的对象之间的联系都通过消息来联系,所以,要定义一种通用的消息格式,让所有的对象都能使用接受。
2、消息的注册
让所有的对象注册感兴趣的消息。
3、消息分发
通过消息总线分发消息,让所有的接收者能收到并处理消息。
接下来就围绕上述这三点进行详细的解析:
1、那通用消息如何定义呢?
主题 + 泛型函数的签名
主题可以是任意一个对象,也可以是任意函数,字符串或者是整型等其他类型。
泛型函数:
std::function<R (Args...)>
std::function作为消息类型
R 函数返回类型
Args… 是一个可变参数模板,代表了任意个数类型的参数
设置好消息格式后,如何注册呢?
消息的注册:
某个对象注册以后,当需要使用的时候,会告诉总线,总线遍历消息列表,查找对应的消息和消息的接收者,找到以后再广播消息。
泛型函数可能是所有的可调用对象,例如:普通函数,成员函数,函数对象,std::function和lamda表达式。要统一这些接口,就是将这些全部转换为std::function。
使用lamda表达式转换为std::function的方法如下:
function_traits.hpp
#pragma once
#include
#include
//普通函数.
//函数指针.
//function/lambda.
//成员函数.
//函数对象.
//转换为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;
};
typedef std::tuple<std::remove_cv_t<std::remove_reference_t<Args>>...> tuple_type;
typedef std::tuple<std::remove_const_t<std::remove_reference_t<Args>>...> bare_tuple_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...)> {};
//member function.
#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())> {};
template <typename Function>
typename function_traits<Function>::stl_function_type to_function(const Function& lambda)
{
return static_cast<typename function_traits<Function>::stl_function_type>(lambda);
}
template <typename Function>
typename 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>
typename function_traits<Function>::pointer to_function_pointer(const Function& lambda)
{
return static_cast<typename function_traits<Function>::pointer>(lambda);
}
#pragma once
#pragma once
#include
#include
#include
#include
struct Any
{
Any(void) : m_tpIndex(std::type_index(typeid(void))) {}
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::decay来移除引用和cv符,从而获取原始类型
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));
}
//将Any转换为实际的类型
template<class U>
U& AnyCast()
{
if (!Is<U>())
{
std::cout << "can not cast " << typeid(U).name() << " to " << m_tpIndex.name() << std::endl;
throw std::logic_error{ "bad cast" };
}
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;
}
Any& operator=(Any&& a)
{
if (m_ptr == a.m_ptr)
return *this;
m_ptr = std::move(a.m_ptr);
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;
};
BasePtr Clone() const
{
if (m_ptr != nullptr)
return m_ptr->Clone();
return nullptr;
}
BasePtr m_ptr;
std::type_index m_tpIndex;
};
2、保存注册消息
使用主题,将需要某个主题的对象收到特定的消息,这才是我们真正想要用的。对比这个,就和QT中的信号槽,发送某个信号,注册对应的发送者和接收者,注册对应的槽函数。
之前说过消息是各种调用对象转换的std::function
std::unordered_multimap
string:键为主题
Any:消息类型的字符串
//注册可调用对象
template <typename F>
void Attach(const string& strTopic, const F& f)
{
auto func = to_function(f);
Add(strTopic,std::move(func));
}
//注册成员函数
template <class C, class... Args, class P>
void Attach(const string& strTopic, void(C::*f)(Args...) const, const P& p)
{
std::function<void(Args...)> func = [&p, f](Args... args){return (*p.*f)(std::forward<Args>(args)...);}
Add(strTopic, std::move(func));
}
template <typename F>
void Add(const string& strTopic, F&&f)
{
string strKey = strTopic + typeid(F).name();
m_map.emplace(std::move(strKey),f);
}
对于非成员函数的可调用对象,先通过to_function将其转换为std::function类型之后再将std::function转换为Any,擦除类型,最后将消息key和消息对象保存起来。
MessageBus.hpp
#pragma once
#include
#include
#include
#include "Any.hpp"
#include "function_traits.hpp"
#include "NonCopyable.hpp"
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);
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 = "")
{
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...)>; //typename function_traits::stl_function_type;
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<string, Any>::iterator Iterater;
};