对象的消息总线——C++11

对象的消息总线——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;
}
  • 0
    点赞
  • 3
    收藏
    觉得还不错? 一键收藏
  • 1
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值