对象的消息总线——C++11
学习《深入应用C++11 代码优化与工程级应用》
什么是消息总线
-
对象之间的关系一般有:依赖、关联、聚合、组合和继承。
-
在大规模的软件开发过程中,对象很多,关联关系非常复杂,如果没有统一的、简洁的方法去管理这些对象关系,会导致对象关系复杂,后期维护困难。
-
基于消息总线技术可以解决这些问题。对象之间只通过消息联系,不是通过直接的依赖或者关联。在消息总线中,对象是通过消息来联系,消息即是对象的关系,只需要在消息总线中管理这些消息,不用关心具体哪些对象之间有关联,便于统一管理。
消息总线包含的内容
通用的消息定义
定义一种通用的消息格式,让所有的对象都能接受。
通用的消息类型完整的定义是主题+泛型函数的签名。主题用来将消息的接收者进行分组,仅仅某个特定组的接收者才能收到消息。泛型函数用来确定哪些接收者具备接收该消息的能力。
-
这里主题用string类型来表示
-
泛型函数为std::function<R(Args…)>
消息的注册
告诉总线该对象对某种消息感兴趣,希望接收到某种主题和类型的消息。总线内部维护了一个管理消息的容器,当需要发送消息时,会遍历该消息容器,从中查找是否有合适的消息和消息接收者。
-
管理消息的容器使用std::multimap<string,Any>m_map,string为主题+消息类型字符串
-
Any类型提供一个可以保存任意类型消息对象的容器
消息的分发
消息总线通过主题发送消息。会查找消息总线内部的容器,找到对这个消息感兴趣的对象,并一一调用,即一一发送消息。
消息总线的实现
设计思路
融合了观察者模式和中介者模式,通过Any类擦除了消息的类型,使得消息总线可以管理所有类型的消息。观察者模式用来维护主题和在适当的时候向观察者广播消;中介者模式主要用来降低观察者模式相互依赖产生的耦合性,使各对象不需要显式地相互引用,而且可以独立地改变它们之间的交互关系。
-
NonCopyable类 防止类被赋值,需要从它派生
-
MessageBus类: 消息总线
-
Any类:存储任何对象的容器
-
function_traits:获取普通函数、函数指针、std::function、函数对象以及成员函数的函数类型、返回类型、参数个数和参数的具体类型。
实现
NonCopyable.hpp
#ifndef C11MSGBUS_NONCOPYABLE_HPP
#define C11MSGBUS_NONCOPYABLE_HPP
//防止类被复制 将复制构造函数和赋值运算符保护起来
class NonCopyable
{
protected:
NonCopyable() =default;
~NonCopyable() =default;
private:
NonCopyable( const NonCopyable& ) =delete;
NonCopyable& operator = (const NonCopyable&) =delete;
};
#endif
Any.hpp
#ifndef C11MSGBUS_ANY_HPP
#define C11MSGBUS_ANY_HPP
#include <iostream>
#include <memory>
#include <typeindex>
using namespace std;
//只能容纳一个元素的容器,可以擦除类型,即可以存储任何类型的值,在使用的时候需要根据实际类型将any对象转换为实际的对象。
//能容纳所以类型的数据,即以一种通用的方式保存所有类型的数据。
//实现方式:通过继承去擦除类型。基类不含模板参数的,派生类才有模板参数,这个模板参数类型正是赋值的类型。
//在赋值时,将创建的派生类对象赋值给基类指针,基类的派生类携带了数据类型,基类只是原始数据的一个占位符,通过多态的隐式转换擦除了原始数据类型,任何数据类型都可以赋值给它,实现能存放所有类型数据的目标。
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(that.Clone()),m_tpIndex(that.m_tpIndex) {}
Any(Any && that): m_ptr(std::move(that.m_ptr)),m_tpIndex(that.m_tpIndex) {} //std::move 将对象的状态或者所有权从一个对象转移到另一个对象,只是转移,没有拷贝
//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>(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>())
{
cout<<" can not cast "<<typeid(U).name()<<" to "<<m_tpIndex.name()<<endl;
throw std::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;
}
private:
struct Base; //存放原始数据的基类
typedef std::unique_ptr<Base> BasePtr; //独占型智能指针,不允许其他的智能指针共享其内部的指针,不允许通过赋值将一个unique_ptr赋值给另外一个unique_ptr
struct Base
{
virtual ~Base(){}
virtual BasePtr Clone() const = 0; //抽象类
};
template<typename T>
struct Derived : Base
{
template<typename U>
Derived(U && value) : m_value(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; //存储类型信息
};
#endif
function_traits.hpp
#ifndef C11MSGBUS_FUNCTION_TRAITS_HPP
#define C11MSGBUS_FUNCTION_TRAITS_HPP
#include <functional>
#include <tuple>
//function_traits 获取普通函数、函数指针、std::function、函数对象以及成员函数的函数类型、返回类型、参数个数和参数的具体类型。
//转换为std::function和函数指针
template<typename T>
struct function_traits;
//普通函数
//返回类型Ret 参数列表Args
template<typename Ret,typename... Args>
struct function_traits<Ret(Args...)>
{
public:
enum { arity=sizeof...(Args)}; //enum 定义参数列表的个数arity常量
//定义别名
typedef Ret function_type(Args...);
typedef Ret return_type;
using std_function_type=std::function<function_type>;
typedef Ret (*pointer)(Args...);
template<size_t I>
struct args
{
//static_assert(常量表达式,错误提示字符串) 静态断言,
static_assert(I<arity,"index is out of range,index less than sizeof Args");
//std::tuple_element 获取元素的类型
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...)>{};
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>
//decltype 类型说明符 作用时选择并返回操作数的数据类型.
struct function_traits:function_traits<decltype(&Callable::operator())>{};
// lambda表达式是一个匿名类,使用一个lambda表达式,实际就是使用一个匿名类的匿名对象,匿名类内部包含operator()调用符。
// operator()的返回类型、形参列表以及 函数体与lambda表达式相对应,因此可像函数调用一样使用lambda表达式。
//可通过function_traits获取lambda表达式的operator()类型,之后再将其转换为std::function
template<typename Function>
typename function_traits<Function>::std_function_type to_function(const Function& lambda)
{
return static_cast<typename function_traits<Function>::std_function_type>(lambda);
}
template<typename Function>
typename function_traits<Function>::std_function_type to_function(Function&& lamdba)
{
return static_cast<typename function_traits<Function>::std_function_type>(std::forward<Function>(lamdba));
}
template<typename Function>
typename function_traits<Function>::pointer to_function_pointer(const Function& lambda)
{
return static_cast<typename function_traits<Function>::pointer>(lambda);
}
#endif
message_bus.hpp
#ifndef C11MSGBUS_MESSAGE_BUS_HPP
#define C11MSGBUS_MESSAGE_BUS_HPP
#include <string>
#include <functional>
#include <map>
#include "Any.hpp"
#include "function_traits.hpp"
#include "NonCopyable.hpp"
using namespace std;
class MessageBus: public 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="")
{
//std::function 函数对象类,包装了满足该函数模板类型的任意函数对象,这些目标实体包括普通函数、Lambda表达式、函数指针、模函数、类成员函数、类静态函数以及其它的函数对象
using function_type=std::function<R()>;
string strMsgType=strTopic+string("##")+typeid(function_type).name();
std::pair<std::multimap<string,Any>::iterator ,std::multimap<string,Any>::iterator> range=m_map.equal_range(strMsgType);
for(Iterator 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+string("##")+typeid(function_type).name();
cout<<"send Msg: "<<strMsgType<<endl;
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)...);
}
}
//移除某个主题,需要主题和消息类型
template<typename R,typename... Args>
void Remove(const string& strTopic="")
{
using function_type=std::function<R(Args...)>;
string strMsgType=strTopic+string("##")+typeid(function_type).name();
cout<<"remove message: "<<strMsgType<<endl;
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+string("##")+typeid(F).name();//主题
//emplace 插入操作,相比inset,其能就地通过参数构造对象,不需要拷贝或者移动内存,使容器插入元素的性能得到进一步提升。
m_map.emplace(std::move(strMsgType),std::forward<F>(f));
cout<<"register message: "<<strMsgType<<endl;
}
private:
std::multimap<string,Any> m_map; //存储注册的消息
typedef std::multimap<string,Any>::iterator Iterator;
};
#endif
测试
main.cpp
#include "message_bus.hpp"
#include <iostream>
#include <string>
using namespace std;
MessageBus g_bus; //全局的消息总线
const string Topic="Drive"; //主题
const string CallBackTopic ="DriveOk"; //执行结果返回主题
struct Subject
{
Subject()
{
//注册执行结果返回的主题
g_bus.Attach([this]{DriveOk();},CallBackTopic);
}
void SendReq(int speed,const string topic)
{
//发送消息
g_bus.SendReq<void,int>(std::move(speed),topic);
}
void DriveOk()
{
cout<<"drive ok"<<endl;
}
};
struct Car
{
Car()
{
//注册主题
g_bus.Attach([this](int speed){Drive(speed);},Topic);
}
void Drive(int speed)
{
cout<<"Car drive: "<<speed<<endl;
//执行结果返回
g_bus.SendReq<void>(CallBackTopic);
}
};
struct Bus
{
Bus()
{
//注册主题
g_bus.Attach([this](int speed){Drive(speed);});
}
void Drive(int speed)
{
cout<<"Bus Drive: "<<speed<<endl;
//执行结果返回
g_bus.SendReq<void>(CallBackTopic);
}
};
struct Truck
{
Truck()
{
//注册主题
g_bus.Attach([this](int speed){Drive(speed);});
}
void Drive(int speed)
{
cout<<"Truck drive: "<<speed<<endl;
//执行结果返回
g_bus.SendReq<void>(CallBackTopic);
}
};
int test()
{
MessageBus bus;
//注册消息
bus.Attach([](int a){cout<<"no reference: "<<a<<endl;});
bus.Attach([](int& a){cout<<"lvalue reference: "<<a<<endl;});
bus.Attach([](int&& a){cout<<"rvalue reference: "<<a<<endl;});
bus.Attach([](const int& a){cout<<"const lvalue reference: "<<a<<endl;});
bus.Attach([](int a){cout<<"no reference has return value and key: "<<a<<endl;return a;},"a");
int i=2;
bus.SendReq<void,int>(2);
bus.SendReq<int,int>(2,"a");
bus.SendReq<void,int&>(i);
bus.SendReq<void,const int&>(2);
bus.SendReq<void,int&&>(2);
bus.Remove<void,int>();
bus.Remove<int,int>("a");
bus.Remove<void,int&>();
bus.Remove<void,const int&>();
bus.Remove<void,int&&>();
bus.SendReq<void,int>(2);
bus.SendReq<int,int>(2,"a");
bus.SendReq<void,int&>(i);
bus.SendReq<void,const int&>(2);
bus.SendReq<void,int&&>(2);
return 0;
}
int main()
{
test();
Subject subject;
Car car;
Bus bus;
Truck truck;
subject.SendReq(20,Topic);
subject.SendReq(50,"");
g_bus.Remove<void,int>();
subject.SendReq(30,"");
return 0;
}