ZLToolKit模块(三)NoticeCenter

功能描述

ZLToolKit的消息广播器,可以广播传递任意个数任意类型参数。
这个类的功能并不复杂,就是添加对某个事件的监听,并设置该事件触发的回调。
有意思的是这个类引入了function_traits这个用于提取函数类型信息的模板类。

测试代码是test_noticeCenter.cpp.


使用示例

#include "Util/NoticeCenter.h"

#define NOTICE_NAME1 "NOTICE_NAME1"

int main()
{
    NoticeCenter::Instance().addListenr(0, NOTICE_NAME1, 
        [](int &a, const char* &b, double &c, string &d) {
            DebugL << a << " " << b << " " << c << " " << d;
            NoticeCenter::Instance()::delListener(0, NOTICE_NAME1);
            
            NoticeCenter::Instance().addListenr(0, NOTICE_NAME1,
                [](int &a, const char* &b, double &c, string &d) {
                    InfoL << a << " " << b << " " << c << " " << d;
                }
        }
    )
    
    int a = 0;
     while(!g_bExitFlag) {
        const char *b = "b";
        double c = 3.14;
        string d("d");
        //每隔1秒广播一次事件,如果无法确定参数类型,可加强制转换
        NoticeCenter::Instance().emitEvent(NOTICE_NAME1,++a,(const char *)"b",c,d);
        sleep(1); // sleep 1 second
    }
    return 0;
}

源码分析

类图

请添加图片描述


源码剖析

  1. EventDispatcher
    事件分发器,负责添加删除监听,触发注册监听的回调。
    其中比较有值得注意和借鉴的两段代码:

     template<typename ...ArgsType>
    int emitEvent(ArgsType &&...args) {
        using funType = std::function<void(decltype(std::forward<ArgsType>(args))...)>;
        decltype(_mapListener) copy;
        {
            //先拷贝(开销比较小),目的是防止在触发回调时还是上锁状态从而导致交叉互锁
            std::lock_guard<std::recursive_mutex> lck(_mtxListener);
            copy = _mapListener;
        }
    
        int ret = 0;
        for (auto &pr : copy) {
            funType *obj = (funType *) (pr.second.get());
            ....
        }
        return ret;
    }
    
    • 防止回调函数进行添加删除,_mapListener修改导致程序出错,这里先拷贝出来。
    • 如果在EventDispatcher锁住了_mtxListener(mtx1)时调用的listener执行添加删除listener操作,会先锁住NoticeCenter里的_mtxListener(mtx1)。
      此时另一个线程正在调用添加删除,先锁住了mtx1等待mtx2,死锁。

    template<typename FUNC>
    void addListener(void *tag, FUNC &&func) {
        function_traits<typename std::remove_reference<FUNC>::type> x;
        using funType = typename function_traits<typename std::remove_reference<FUNC>::type>::stl_function_type;
        std::shared_ptr<void> pListener(new funType(std::forward<FUNC>(func)), [](void *ptr) {
            funType *obj = (funType *) ptr;
            delete obj;
        });
        //...
        }
    

    function_traits这个类可以归一所有函数类型的对象,后面C++部分有详细分析。

  2. NoticeCenter
    事件管理中心,单例。
    负责创建EventDispatcher事件分发器、调用创建的EventDispatcher执行添加listener、删除listenr、触发事件等操作。


C++11

using和typedef的区别

using可以直接定义模板函数的别名

template<typename T>
using ptr = std::unique_ptr<T>;

ptr<int> p(new int(10));     //正确

template<typename T>
typedef std::unique_ptr<T> ptr; //错误

template<typename T>
struct A {
	typedef std::unique_ptr<T> ptr;
};

A<int>::ptr x(new int(10));  //正确

所以感觉区别不大。

function_traits

template<typename T>
struct function_traits;

//普通函数, lambda表达式
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;
    };
};

//函数指针
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())>{};

可以通过这个类获取将任何函数类型转为stl的function类型。也可以活获取参数类型。
在noticeCenter里有如下使用:

template<typename FUNC>
    void addListener(void *tag, FUNC &&func) {
        using funType = typename function_traits<typename std::remove_reference<FUNC>::type>::stl_function_type;
        std::shared_ptr<void> pListener(new funType(std::forward<FUNC>(func)), [](void *ptr) {
            funType *obj = (funType *) ptr;
            delete obj;
        });
        std::lock_guard<std::recursive_mutex> lck(_mtxListener);
        _mapListener.emplace(tag, pListener);
    }

这里通过function_traits的stl_function_type得到stl的function类型,并构造了该类型的对象。

function

可以存储各种函数、函数指针、lambda 表达式等可调用对象。
例如,你可以使用 std::function<int(int, float)>来存储一个参数类型为int和float,返回值类型为int的可调用对象。

  • 将函数、函数指针、lambda 表达式等存储在一个变量中,方便传递和调用;
  • 在不确定可调用对象的具体类型的情况下,接受各种类型的可调用对象;
  • 在运行时动态绑定可调用对象;
  • 用于实现回调函数等;
  • 可以使用std::bind 将函数或函数对象绑定到一个function变量中

例如:

#include <functional>

int func(int x, float y) {
    return static_cast<int>(x + y);
}

int func2(int x, int y, int z) {
    return x + y + z;
}


struct FuncObj {
    int operator()(int x, float y) {
        return static_cast<int>(x * y);
    }
};

int main()
{
    std::function<int(int, float)> f1 = func;
    f1(1, 2.5f);  // Output: 3

    std::function<int(int, float)> f2 = [](int x, float y) { return static_cast<int>(x * y); };
    f2(3, 0.5f);  // Output: 1
    
    std::function<int(int, float)> f3 = std::bind(func, std::placeholders::_1, std::placeholders::_2);
    std::cout << f1(1, 2.5f) << std::endl;   // Output: 3
    
    std::function<int(int, float)> f4 = std::bind(FuncObj(), std::placeholders::_1, std::placeholders::_2);
    std::cout << f4(3, 0.5f) << std::endl;  // Output: 1
    
    std::function<int(int)> f5 = std::bind(func2, 1, 2, std::placeholders::_1);
    std::cout << f5(3) << std::endl;  // Output: 6
    
    return 0;
}

tuple_element

可以用于提取元组类型中某一位置的元素的类型。

using TupleType = std::tuple<int, float, double>;
using ElementType = std::tuple_element<1, TupleType>::type;
std::cout << typeid(ElementType).name() << std::endl;  // Output: "float"
  • 3
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值