• C++11开发的消息总线库


    消息总线是一种降低耦合的一种技术,对象间只通过消息练习,不通过依赖或者关联。将复杂对象关系简化,降低复杂度,提高程序的维护性。
    消息总线是一种解耦合的程序的设计方式,可以让对象之间的依赖和继承用消息注册来联系。

    消息总线的关键技术
    1、通用消息的定义
    本质其实是让所有的对象之间的联系都通过消息来联系,所以,要定义一种通用的消息格式,让所有的对象都能使用接受。
    2、消息的注册
    让所有的对象注册感兴趣的消息。
    3、消息分发
    通过消息总线分发消息,让所有的接收者能收到并处理消息。

    接下来就围绕上述这三点进行详细的解析:
    1、那通用消息如何定义呢?

    主题 + 泛型函数的签名

    主题可以是任意一个对象,也可以是任意函数,字符串或者是整型等其他类型。

    泛型函数:

    std::function<R (Args...)>
    
    • 1

    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;
    };
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31
    • 32
    • 33
    • 34
    • 35
    • 36
    • 37
    • 38
    • 39
    • 40
    • 41
    • 42
    • 43
    • 44
    • 45
    • 46
    • 47
    • 48
    • 49
    • 50
    • 51
    • 52
    • 53
    • 54
    • 55
    • 56
    • 57
    • 58
    • 59
    • 60
    • 61
    • 62
    • 63
    • 64
    • 65
    • 66
    • 67
    • 68
    • 69
    • 70
    • 71
    • 72
    • 73
    • 74
    • 75
    • 76
    • 77
    • 78
    • 79
    • 80
    • 81
    • 82
    • 83
    • 84
    • 85
    • 86
    • 87
    • 88
    • 89
    • 90
    • 91
    • 92
    • 93
    • 94
    • 95
    • 96
    • 97
    • 98
    • 99
    • 100
    • 101
    • 102
    • 103
    • 104
    • 105
    • 106
    • 107
    • 108
    • 109
    • 110
    • 111
    • 112
    • 113
    • 114
    • 115
    • 116
    • 117
    • 118
    • 119
    • 120
    • 121
    • 122
    • 123
    • 124
    • 125
    • 126
    • 127
    • 128
    • 129
    • 130
    • 131
    • 132
    • 133
    • 134
    • 135
    • 136
    • 137
    • 138
    • 139
    • 140
    • 141
    • 142
    • 143
    • 144
    • 145
    • 146
    • 147
    • 148
    • 149
    • 150
    • 151
    • 152
    • 153
    • 154
    • 155
    • 156
    • 157
    • 158
    • 159
    • 160
    • 161
    • 162
    • 163
    • 164
    • 165
    • 166
    • 167
    • 168
    • 169
    • 170
    • 171
    • 172
    • 173
    • 174
    • 175
    • 176
    • 177

    2、保存注册消息

    使用主题,将需要某个主题的对象收到特定的消息,这才是我们真正想要用的。对比这个,就和QT中的信号槽,发送某个信号,注册对应的发送者和接收者,注册对应的槽函数。

    之前说过消息是各种调用对象转换的std::function类型,所以这些消息可能是各种各样的,就需要就不同类型 的消息保存起来,但是C++又不能直接保存各种类型到一个容器里,所以将对象的类型擦除(使用Any类型来擦除对象的类型)。

    std::unordered_multimapm_map
    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);
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22

    对于非成员函数的可调用对象,先通过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;
    };
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31
    • 32
    • 33
    • 34
    • 35
    • 36
    • 37
    • 38
    • 39
    • 40
    • 41
    • 42
    • 43
    • 44
    • 45
    • 46
    • 47
    • 48
    • 49
    • 50
    • 51
    • 52
    • 53
    • 54
    • 55
    • 56
    • 57
    • 58
    • 59
    • 60
    • 61
    • 62
    • 63
    • 64
    • 65
    • 66
    • 67
    • 68
    • 69
    • 70
    • 71
    • 72
    • 73
  • 相关阅读:
    <sa8650>qcxser 之 QCarCam 6.X API介绍 (第一部分)
    Android Studio实现内容丰富的安卓养老平台
    记录一下 cuda、torchinfo、gpustat 相关知识
    教程图文详解 - 广域网通信(第三章)
    欧拉图(Euler Graph)
    【真题T1】[NOIP2021] 报数
    react native中如何使用webView调用腾讯地图选点组件
    Spark - RDD 算子介绍及使用 Scala、Java、Python 三种语言演示
    JS中字体文件的加载和使用
    python读取Excel到mysql
  • 原文地址:https://blog.csdn.net/qq_42317817/article/details/122112195