栏目分类:
子分类:
返回
名师互学网用户登录
快速导航关闭
当前搜索
当前分类
子分类
实用工具
热门搜索
名师互学网 > IT > 系统运维 > 运维 > Linux

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

Linux 更新时间: 发布时间: IT归档 最新发布 模块sitemap 名妆网 法律咨询 聚返吧 英语巴士网 伯小乐 网商动力

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

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

学习《深入应用C++11 代码优化与工程级应用》

什么是消息总线
  • 对象之间的关系一般有:依赖、关联、聚合、组合和继承。

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

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

消息总线包含的内容 通用的消息定义

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

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

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

  • 泛型函数为std::function

消息的注册

告诉总线该对象对某种消息感兴趣,希望接收到某种主题和类型的消息。总线内部维护了一个管理消息的容器,当需要发送消息时,会遍历该消息容器,从中查找是否有合适的消息和消息接收者。

  • 管理消息的容器使用std::multimapm_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 
#include 
#include 

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::type,Any>::value,U>::type>
    Any(U && value):m_ptr(new Derived::type>(forward(value))),m_tpIndex(std::type_index(typeid(typename std::decay::type))) {}

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

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

    template
    U& AnyCast()
    {
        if(!Is())
        {
            cout<<" can not cast "<*>(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 basePtr;  //独占型智能指针,不允许其他的智能指针共享其内部的指针,不允许通过赋值将一个unique_ptr赋值给另外一个unique_ptr

    struct base
    {
        virtual ~base(){}

        virtual basePtr Clone() const = 0;  //抽象类
    };

    template
    struct Derived : base
    {
        template
        Derived(U && value) : m_value(forward(value)) {}

        basePtr Clone() const
        {
            return basePtr(new Derived(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 
#include 

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

//转换为std::function和函数指针
template
struct function_traits;

//普通函数
//返回类型Ret 参数列表Args
template
struct function_traits
{
public:
    enum { arity=sizeof...(Args)}; //enum 定义参数列表的个数arity常量
    //定义别名
    typedef Ret function_type(Args...);
    typedef Ret return_type;
    using std_function_type=std::function;
    typedef Ret (*pointer)(Args...);

    template
    struct args
    {
        //static_assert(常量表达式,错误提示字符串) 静态断言,
        static_assert(I>::type;
    };
};

template
struct function_traits:function_traits{};

template
struct function_traits>:function_traits{};

#define FUNCTION_TRAITS(...) 
    template  
    struct function_traits:function_traits{}; 

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

//函数对象
template
//decltype  类型说明符 作用时选择并返回操作数的数据类型.
struct function_traits:function_traits{};

// lambda表达式是一个匿名类,使用一个lambda表达式,实际就是使用一个匿名类的匿名对象,匿名类内部包含operator()调用符。
// operator()的返回类型、形参列表以及 函数体与lambda表达式相对应,因此可像函数调用一样使用lambda表达式。
//可通过function_traits获取lambda表达式的operator()类型,之后再将其转换为std::function

template
typename function_traits::std_function_type to_function(const Function& lambda)
{
    return static_cast::std_function_type>(lambda);
}

template
typename  function_traits::std_function_type to_function(Function&& lamdba)
{
    return static_cast::std_function_type>(std::forward(lamdba));
}

template
typename function_traits::pointer to_function_pointer(const Function& lambda)
{
    return static_cast::pointer>(lambda);
}
#endif

message_bus.hpp

#ifndef C11MSGBUS_MESSAGE_BUS_HPP
#define C11MSGBUS_MESSAGE_BUS_HPP

#include 
#include 
#include 
#include "Any.hpp"
#include "function_traits.hpp"
#include "NonCopyable.hpp"

using namespace std;

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

    //发送消息
    template  //无参数
    void SendReq(const string& strTopic="")
    {
        //std::function  函数对象类,包装了满足该函数模板类型的任意函数对象,这些目标实体包括普通函数、Lambda表达式、函数指针、模函数、类成员函数、类静态函数以及其它的函数对象
        using function_type=std::function;
        string strMsgType=strTopic+string("##")+typeid(function_type).name();

        std::pair::iterator ,std::multimap::iterator> range=m_map.equal_range(strMsgType);
        for(Iterator it=range.first;it!=range.second;++it)
        {
            auto f=it->second.AnyCast();
            f();
        }
    }

    template  //有参数
    void SendReq(Args&&... args,const string& strTopic="")
    {
        using function_type=std::function;
        string strMsgType=strTopic+string("##")+typeid(function_type).name();
        cout<<"send Msg: "<second.AnyCast();
            f(std::forward(args)...);
        }
    }

    //移除某个主题,需要主题和消息类型
    template
    void Remove(const string& strTopic="")
    {
        using function_type=std::function;
        string strMsgType=strTopic+string("##")+typeid(function_type).name();
        cout<<"remove message: "<
    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));

        cout<<"register message: "< m_map; //存储注册的消息
    typedef std::multimap::iterator Iterator;


};

#endif

测试

main.cpp

#include "message_bus.hpp"

#include 
#include 

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(std::move(speed),topic);
    }

    void DriveOk()
    {
        cout<<"drive ok"<(CallBackTopic);
    }
};

struct Bus
{
    Bus()
    {
        //注册主题
        g_bus.Attach([this](int speed){Drive(speed);});
    }

    void Drive(int speed)
    {
        cout<<"Bus Drive: "<(CallBackTopic);
    }
};

struct Truck
{
    Truck()
    {
        //注册主题
        g_bus.Attach([this](int speed){Drive(speed);});
    }

    void Drive(int speed)
    {
        cout<<"Truck drive: "<(CallBackTopic);
    }
};

int test()
{
    MessageBus bus;

    //注册消息
    bus.Attach([](int a){cout<<"no reference: "<(2);
    bus.SendReq(2,"a");
    bus.SendReq(i);
    bus.SendReq(2);
    bus.SendReq(2);

    bus.Remove();
    bus.Remove("a");
    bus.Remove();
    bus.Remove();
    bus.Remove();

    bus.SendReq(2);
    bus.SendReq(2,"a");
    bus.SendReq(i);
    bus.SendReq(2);
    bus.SendReq(2);

    return 0;
}

int main()
{
    test();

    Subject subject;
    Car car;
    Bus bus;
    Truck truck;

    subject.SendReq(20,Topic);
    subject.SendReq(50,"");

    g_bus.Remove();
    subject.SendReq(30,"");

    return 0;
}
转载请注明:文章转载自 www.mshxw.com
我们一直用心在做
关于我们 文章归档 网站地图 联系我们

版权所有 (c)2021-2022 MSHXW.COM

ICP备案号:晋ICP备2021003244-6号