功能描述
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;
}
源码分析
类图
源码剖析
-
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++部分有详细分析。
-
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"