使用C++标准库简单实现事件触发机制 Event.h
/*
Author:StubbornHuang
Data:2023.1.31
Email:stubbornhuang@qq.com
*/
#ifndef _EVENT_H_
#define _EVENT_H_
#include <functional>
#include <map>
#include <type_traits>
#ifndef EVENT_NO_THREAD_SAFETY
#define EVENT_THREAD_SAFETY
#endif // !EVENT_NO_THREAD_SAFETY
#ifdef EVENT_THREAD_SAFETY
#include <atomic>
#include <mutex>
#endif // EVENT_THREAD_SAFETY
#ifdef EVENT_THREAD_SAFETY
#define DELEGATE_ID_TYPE std::atomic_uint64_t
#else
#define DELEGATE_ID_TYPE std::uint64_t
#endif // EVENT_THREAD_SAFETY
namespace stubbornhuang
{
static DELEGATE_ID_TYPE DELEGATE_ID = 1;
template<typename Prototype> class Event;
template<typename ReturnType, typename ...Args>
class Event <ReturnType(Args...)>
{
private:
using return_type = ReturnType;
using function_type = ReturnType(Args ...);
using std_function_type = std::function<function_type>;
using function_pointer = ReturnType(*)(Args...);
private:
class Delegate
{
public:
Delegate() = delete;
Delegate(int id,std_function_type std_function_func)
:m_Handler(nullptr),m_Id(-1)
{
if (std_function_func == nullptr)
return;
m_Id = id;
m_Handler = std_function_func;
}
void Invoke(Args ...args)
{
if (m_Handler != nullptr)
{
m_Handler(args...);
}
}
private:
int m_Id;
std_function_type m_Handler;
};
public:
int AddDelegate(std_function_type std_function_func)
{
if (std_function_func == nullptr)
return -1;
std::shared_ptr<Delegate> pDelegate = std::make_shared<Delegate>(DELEGATE_ID, std_function_func);
#ifdef EVENT_THREAD_SAFETY
std::lock_guard<std::mutex> guard_mutex(m_event_mutex);
#endif // EVENT_THREAD_SAFETY
m_delegates.insert(std::pair<int, std::shared_ptr<Delegate>>(DELEGATE_ID, pDelegate));
return DELEGATE_ID++;
}
bool RemoveDelegate(int delegate_id)
{
#ifdef EVENT_THREAD_SAFETY
std::lock_guard<std::mutex> guard_mutex(m_event_mutex);
#endif // EVENT_THREAD_SAFETY
if (m_delegates.count(delegate_id) == 0)
return false;
m_delegates.erase(delegate_id);
return true;
}
int operator += (std_function_type std_function_func)
{
return AddDelegate(std_function_func);
}
bool operator -= (int delegate_id)
{
return RemoveDelegate(delegate_id);
}
void Invoke(Args ...args)
{
#ifdef EVENT_THREAD_SAFETY
std::lock_guard<std::mutex> guard_mutex(m_event_mutex);
#endif // EVENT_THREAD_SAFETY
for (const auto& key : m_delegates)
{
key.second->Invoke(args...);
}
}
bool Invoke(int delegate_id, Args ...args)
{
#ifdef EVENT_THREAD_SAFETY
std::lock_guard<std::mutex> guard_mutex(m_event_mutex);
#endif // EVENT_THREAD_SAFETY
if (m_delegates.count(delegate_id) == 0)
return false;
m_delegates[delegate_id]->Invoke(args...);
return true;
}
void operator() (Args ...args)
{
#ifdef EVENT_THREAD_SAFETY
std::lock_guard<std::mutex> guard_mutex(m_event_mutex);
#endif // EVENT_THREAD_SAFETY
for (const auto& key : m_delegates)
{
key.second->Invoke(args...);
}
}
bool operator() (int delegate_id, Args ...args)
{
#ifdef EVENT_THREAD_SAFETY
std::lock_guard<std::mutex> guard_mutex(m_event_mutex);
#endif // EVENT_THREAD_SAFETY
if (m_delegates.count(delegate_id) == 0)
return false;
m_delegates[delegate_id]->Invoke(args...);
return true;
}
int GetDelegateSize()
{
#ifdef EVENT_THREAD_SAFETY
std::lock_guard<std::mutex> guard_mutex(m_event_mutex);
#endif // EVENT_THREAD_SAFETY
return m_delegates.size();
}
private:
std::map<int, std::shared_ptr<Delegate>> m_delegates;
#ifdef EVENT_THREAD_SAFETY
std::mutex m_event_mutex;
#endif // EVENT_THREAD_SAFETY
};
}
#endif // !_EVENT_H_
在上述代码中我们使用template<typename ReturnType, typename ...Args>
对事件类Event
进行了模板化,使用变参模板typename ...Args
自定义事件绑定的委托函数参数列表,可以接受多个不同类型的参数。使用std::vector
存储绑定事件的std::function<ReturnType(Args...)>
的委托函数,并重载+=
操作符添加委托函数。
上述事件工具类Event
的使用示例如下:
不带参数
#include <iostream>
#include "Event.h"
class Button
{
public:
Button()
{
}
virtual~Button()
{
}
public:
stubbornhuang::Event<void()> OnClick;
};
void Click()
{
std::cout << "Button Click" << std::endl;
}
class Example
{
public:
void Click()
{
std::cout << "Example Click" << std::endl;
}
};
int main()
{
Button button;
button.OnClick += Click; // 静态函数做委托函数
Example example;
button.OnClick += std::bind(&Example::Click, example); // 成员函数做委托函数
button.OnClick += []() { std::cout << "Lambda Click" << std::endl; }; // 匿名函数做委托函数
button.OnClick();
return 0;
}
带参数:
#include <iostream>
#include "event.h"
void Sum(int a, int b)
{
std::cout << "Sum = " << a + b << std::endl;
}
class Example
{
public:
Example()
{
}
virtual~ Example()
{
}
void Print(int a, int b)
{
std::cout << "a = " << a <<" " << "b = " << b << std::endl;
}
};
int main()
{
stubbornhuang::Event<void(int, int)> event;
std::cout << "-----Add Delegate-----" << std::endl;
int sum_fuc_delegate_id = event.AddDelegate(Sum); // static function
event += [](int a, int b) { std::cout << "Sub = " << a - b << std::endl; }; // lambda function
Example example;
event += std::bind(&Example::Print, example, std::placeholders::_1, std::placeholders::_2); // class member function
event(1,5);
std::cout<<std::endl << "-----Remove Delegate-----" << std::endl;
event -= (sum_fuc_delegate_id);
event(1, 5);
return 0;
}
该类默认是线程安全的,如果不想要线程安全,可以在包含头文件之前定义EVENT_NO_THREAD_SAFETY
即可,该类同时也是模板类,支持定义任何返回类型和可变参数的事件类,并通过返回委托函数ID,对委托函数进行删除操作。