C++ & FastDelegate实现可快速部署的事件触发机制

本文介绍了一种基于C++的事件触发机制,采用FastDelegate库实现,支持快速部署、任意参数类型及数量的事件,以及不同类型的回调函数注册。设计上降低了与项目的耦合度,实现了独立部署。

C++ & FastDelegate实现可快速部署的事件触发机制

前言

事件触发机制是面向组件编程的重要部分,同时也不失为一种贴近正常人思考方式的编程模式。但是C++在这方面的支持仍旧不足,同时根据现有的C++标准也很难轻易写出功能全面,部署简易的事件触发机制。我在近几周以来查阅了各类C++事件触发机制的写法,发现有如下问题:1)事件模块与整个项目耦合度过高,往往需要使用者以某种方式构建自己的类并赋予事件相关信息,或者在合适的类根据事件不同创建不同的信号量。2)往往不能做到同一个函数被不同种的事件回调,以及不能做到不定参数的回调。3)回调函数可以分为类成员函数,类静态函数,普通函数;但是往往不能一视同仁地对待这三种类型的回调函数。
对此,本文构建的事件触发机制模块将具有以下特性:1)快速,独立的部署方式,仅仅在模块内部,通过简洁的语句就可以适配不同的项目。2)支持任意数目,任意类型的事件参数;支持事件按主键触发,主键可选为任意值触发。3)针对三类同的回调函数类型,都可以一致地进行注册和回调。
注:本文的事件触发机制不涉及多线程,但可以通扩展事件触发队列功能来实现对多线程的支持。

使用示例

事件模块部署

//Event.cpp
void EventSystem::Init()
{
 if (isInit)
  return;
 AddEvent(EVENT_TEST, EventInt, int); 	//通过此方式增加事件类型,分别为事件名,事件主键名,
 					//事件主键类型
 isInit = true;
 }
 
void EventData::Init()
{
 if (isInit)
  return;
 AddEventData(Name, string); //通过此方式增加事件触发数据
 AddEventData(EventName, string);
 AddEventData(TriggerEvent, Event*);
 AddEventData(TriggerItem, DWORD);
 isInit = true;
}

对一个新的项目来说,你只需在 Event.cppEventSystem::Init() 中调用 AddEvent(事件名,事件主键名, 事件主键类型) 即可创建一个新的事件类型。不过,实际上 AddEvent 是一个宏。同时,这里在设定类型后,事件系统实际储存的是该变量的地址,即系统实际以地址作为主键,类型仅仅用于复原变量,所以在某个变量被作为主键但又被销毁后,实际上会导致这个事件出错(只要不用野指针回调就不会导致崩溃,只要确保变量销毁时同步注销事件就可以确保正确)。
同时,在 Event.cppEventData::Init() 中调用 AddEventData(参数名, 参数类型) 即可创建一个新的事件参数。其中 EventName , TriggerEvent , TriggerItem 都为系统保留参数,分别代表触发的事件名称,触发的事件对象,以及触发的主键(任意类型)。

注册&回调&注销

//main.cpp
#include"Event.h"
int a = 0;
Event* e;
void test(EventData*arg) {
 EventSystem::DeleteEvent(e);
 string name;
 arg->get("Name", name);
 string eventName;
 arg->get("EventName", eventName);
 int*t;
 arg->get("EventInt", t);
 int* t1;
 arg->get("TriggerItem", t1);
 if(t)
  printf("%s %s %d %d\n", name.c_str(), eventName.c_str(), *t, *t1);
 else {
  if(t1)
   printf("%s %s ACTIVATE_ALL %d\n", name.c_str(), eventName.c_str(), *t1);
  else
   printf("%s %s ACTIVATE_ALL NULL\n", name.c_str(), eventName.c_str());
 }
}

int main() {
 Action t = test;
 e = EventSystem::RegistEvent("EVENT_TEST", t, a);
 a = 10;
 int b = 5;
 EventSystem::RegistEvent("EVENT_TEST", t, b);
 EventSystem::RegistEvent("EVENT_TEST", t, ANY_VALUE);
 
 EventSystem::CallBack("EVENT_TEST", ANY_VALUE, make_pair("Name", string("test"))); //唤醒该类事件中任意值事件
 printf("\n");
 EventSystem::CallBack("EVENT_TEST", b, make_pair("Name", string("test"))); //唤醒该类事件中特定主键事件
 EventSystem::Clean();			//事件系统的内存清理
}

所有回调目标函数都型为 void(EventData*) , Action 即为该型函数指针,如果目标是成员函数,则需使用 bind (详细查看 FastDelegrate1 使用方式)。在本例中, EVENT_TEST 被设定为针对 int 变量的事件;总共注册了三个事件分别以变量 a , b 以及 任意值 作为主键;同时进行了 任意值b 两次主键的回调(任意值主键只会触发以任意值为主键的事件,而其余任何指定主键也同样会触发任意值主键事件)。在回调时,如果需要添加参数,仅需在末尾添加若干 make_pair , CallBack 函数为可变参函数。
test 回调目标函数中,可以看到事件参数的获取,在获取 TriggerItem 时,仅需传入主键类型的指针即可( EventInt 代表着注册时主键, TriggerItem 代表着触发事件的变量,所以在任意值主键事件时你可以获取每次触发事件的变量)。
同时,在 test 回调目标函数中,你可以看到我调用了 EventSystem::DeleteEvent 在回调目标函数中进行了事件删除;你同样可以在回调目标函数中注册新的事件,但是在回调目标函数中对事件的注册注销不会影响该次被触发的事件(在回调过程中删除本该被触发的事件并不影响该事件的响应)。如果你只是想让某个事件时效一段时间,可以选择使用 EventSystem::SetEnable 函数,该函数可以设定事件是否可被触发。
本次测试输出结果见下图。

设计细节

首先简要分析事件触发机制。事件究其根本为程序运行中出现特定的情况,落到细处则是特定代码段在特定环境下的运行,而某个事件类型的内涵实际上是我们人为赋予的,而事件的生命则是由回调赋予。事件触发机制本质上是对代码在抽象层次的动态调度、是对回调机制的聚集和注释。
同时,本模块致力于降低事件触发机制与项目的耦合度,实现快速且相对更独立地部署。故有如下基本设定:

  • 事件类型由名字和主键组成
  • 回调通过字典形式传参
  • 事件的注册,回调均需通过模板函数适配全部事件类

实现代码

//Event.h
#pragma once
#include <any>
#include <map>
#include <string>
#include <set>
#include <vector>
#include <queue>
#include "FastDelegate.h"
using namespace std;
using namespace fastdelegate;
#define AddEventData(name, type) getArgFunc[#name].bind([](map<string, any>* data, void* tmp) {\
  type* t = (type*)tmp;\
  if(!(*data)[#name]._Cast<type>())\
   *t = (type)0x00000000;\
  else\
   *t = *((*data)[#name]._Cast<type>()); \
 })
#define AddEvent(name, mk, type) static EventType* name = new EventType(#name, #mk);\
 EventSystem::EventMap[#name] = multimap<DWORD, Event*>();\
 FastDelegate2< map<string, any>*, void*> func##name = (FastDelegate2< map<string, any>*, void*>)(\
  [](map<string, any>* data, void* tmp) {\
   type** t = (type**)tmp;\
   *t = *((*data)[#mk]._Cast<type*>());\
  });\
 EventData::AddMainkey(#mk, func##name)
#define ANY_VALUE *(int*)nullptr

class EventData;
typedef unsigned long DWORD;
typedef FastDelegate1< EventData* >Action;

struct EventType {
 string eventName; //事件名
 string mainKey; //区分同类事件的主键
 static map<string, EventType*>EventTypeMap; //全部事件类型
 EventType(string n, string mk) {
  this->eventName = n;
  this->mainKey = mk;
  EventTypeMap[n] = (this);
 }
};

class EventData {
 friend class EventSystem;
private:
 map<string, any> data; //事件参数,由参数名字和值组成
 static map<string, FastDelegate2< map<string, any>*, void*>>getArgFunc; //不同参数的获取函数映射表
 static bool isInit; //该类是否初始化,用于静默初始化

 static void Init(); //类初始化
 static void AddMainkey(string mk, FastDelegate2< map<string, any>*, void*>& func) {
  if (getArgFunc.find(mk) == getArgFunc.end()) {
   getArgFunc[mk] = (func);
  }
 }
public:
 EventData() {
  if (!isInit) {
   Init();
   isInit = true;
  }
 }

 void put(string key, any value) { //增加事件参数
  data[key] = value;
 }

 template<class T>
 void get(string key, T& tmp) { //获取事件参数
  getArgFunc[key](&this->data, (void*)&tmp);
 }
};

class Event {
 friend class EventSystem;
private:
 static map<string, multimap<DWORD, Event*>> EventMap; //全部事件二级映射表,一级为事件类型,二级为事件主键
 static bool isInit; //该类是否初始化,用于静默初始化
 static queue<Event*>delQ; //删除队列
 static int isTrigger; //正在处理触发
 
 void PutArg(string key, any value) { //添加参数
  eventData.put(key, value);
 }

 void Exec() { //执行目标函数
  if (!enable)
   return;
  this->tarFunc(&this->eventData);
 }
 
public:
 Event(const EventType& et, Action func, any& key, bool enable=true) { //注册一个事件
  eventData.put(et.mainKey, key);
  this->eventTypeName = et.eventName;
  this->tarFunc = func;
  this->enable = true;
 }

 void SetEnable(bool flag) {
  this->enable = flag;
 }
};

class EventSystem { //事件系统,静态类
private:
 static map<string, multimap<DWORD, Event*>> EventMap; //全部事件二级映射表,一级为事件类型,二级为事件主键
 static bool isInit; //该类是否初始化,用于静默初始化
 static queue<Event*>delQ; //删除队列
 static bool isTrigger; //正在处理触发

 static void Init(); //类初始化

 static void DeleteEventQ() {
  while (!delQ.empty()) {
   DeleteEvent(delQ.front());
   delQ.pop();
  }
 }

public:
 template<class ... Args, class T>
 static Event* RegistEvent(const string& en, Action func, T& key, Args...args) { //注册新的事件
  if (!isInit)
   Init();
  vector<pair<string, any>>* arg = new vector<pair<string, any>>;
  int a[] = { (arg->push_back(args),0)... }; //保存参数
  EventType* et = EventType::EventTypeMap[en];
  any t = &key;
  DWORD vt = (DWORD)(void*)&key;
  Event* e = new Event(*et, func, t);
  for (auto para : *arg)
   e->PutArg(para.first, para.second);
  EventMap[et->eventName].insert({ vt, e });
  delete arg;
  return e;
 }
 
 template<class T>
 static Event* RegistEvent(const string& en, Action func, T& key) { //注册新的事件
  if (!isInit)
   Init();
  EventType* et = EventType::EventTypeMap[en];
  any t = &key;
  DWORD vt = (DWORD)(void*)&key;
  Event* e = new Event(*et, func, t);
  EventMap[et->eventName].insert({ vt, e });
  return e;
 }

 template<class ... Args, class T>
 static void CallBack(const string& en, T&tkey, Args...args) //事件回调
 {
  if (!isInit)
   Init();
  isTrigger = true;
  EventType* et = EventType::EventTypeMap[en];
  DWORD key = (DWORD)(void*)&tkey;
  vector<pair<string, any>>* arg = new vector<pair<string, any>>;
  int a[] = { (arg->push_back(args),0)... }; //保存参数
  string eName = et->eventName;
  auto triBeg = EventMap[eName].lower_bound(key);
  auto triEnd = EventMap[eName].upper_bound(key);
  while (triBeg != triEnd) {
   Event* e = triBeg->second;
   for (auto para : *arg)
    e->PutArg(para.first, para.second);
   e->PutArg("EventName", en);
   e->PutArg("TriggerEvent", e);
   e->PutArg("TriggerItem", key);
   e->Exec();
   triBeg++;
  }
  if (&tkey == nullptr) {
   isTrigger--;
   DeleteEventQ();
   return;
  }
  triBeg = EventMap[eName].lower_bound((DWORD)(void*)&ANY_VALUE);
  triEnd = EventMap[eName].upper_bound((DWORD)(void*)&ANY_VALUE);
  while (triBeg != triEnd) {
   Event* e = triBeg->second;
   for (auto para : *arg)
    e->PutArg(para.first, para.second);
   e->PutArg("EventName", en);
   e->PutArg("TriggerEvent", e);
   e->PutArg("TriggerItem", key);
   e->Exec();
   triBeg++;
  }
  isTrigger = false;
  DeleteEventQ();
  delete arg;
 }

 template<class T>
 static void CallBack(const string& en, T& tkey) //事件回调,无附加参数版本
 {
  if (!isInit)
   Init();
  isTrigger++;
  EventType* et = EventType::EventTypeMap[en];
  DWORD key = (DWORD)(void*)&tkey;
  string eName = et->eventName;
  auto triBeg = EventMap[eName].lower_bound(key);
  auto triEnd = EventMap[eName].upper_bound(key);
  while (triBeg != triEnd) {
   Event* e = triBeg->second;
   e->PutArg("EventName", en);
   e->PutArg("TriggerEvent", e);
   e->PutArg("TriggerItemAdress", key);
   e->Exec();
   triBeg++;
  }
  if (&tkey == nullptr) {
   isTrigger--;
   DeleteEventQ();
   return;
  }
  triBeg = EventMap[eName].lower_bound((DWORD)(void*)&ANY_VALUE);
  triEnd = EventMap[eName].upper_bound((DWORD)(void*)&ANY_VALUE);
  while (triBeg != triEnd) {
   Event* e = triBeg->second;
   e->PutArg("EventName", en);
   e->PutArg("TriggerEvent", e);
   e->PutArg("TriggerItemAdress", key);
   e->Exec();
   triBeg++;
  }
  isTrigger--;
  DeleteEventQ();
 }

 static void DeleteEvent(Event* e) {
  if (isTrigger) {
   delQ.push(e);
   return;
  }
  auto triBeg = EventMap[e->eventTypeName].begin();
  auto triEnd = EventMap[e->eventTypeName].end();
  while (triBeg != triEnd) {
   Event* et = triBeg->second;
   if (et == e) {
    EventMap[e->eventTypeName].erase(triBeg);
    break;
   }
   triBeg++;
  }
 }

 static void Clean() {
  for (auto type : EventType::EventTypeMap) {
   EventSystem::EventMap[type.first].erase(EventSystem::EventMap[type.first].begin(), EventSystem::EventMap[type.first].end());
   delete type.second;
  }
 }
};
//Event.cpp
#include "Event.h"
map<string, EventType*>EventType::EventTypeMap;
map<string, FastDelegate2< map<string, any>*, void*>>EventData::getArgFunc;
bool EventData::isInit = false;
map<string, multimap<DWORD, Event*>> EventSystem::EventMap;
bool EventSystem::isInit = false;
queue<Event*>EventSystem::delQ;
int EventSystem::isTrigger = 0;

void EventSystem::Init()
{
 if (isInit)
  return;
 AddEvent(EVENT_TEST, EventInt, int); //通过此方式增加事件类型,分别为事件名,事件主键名,事件主键类型(自动变为该类型的指针)
 isInit = true;
}

void EventData::Init()
{
 if (isInit)
  return;
 AddEventData(Name, string); //通过此方式增加事件触发数据
 AddEventData(EventName, string);
 AddEventData(TriggerEvent, Event*);
 AddEventData(TriggerItem, DWORD);
 isInit = true;
}

  1. 链接: https://github.com/dreamcat4/FastDelegate. ↩︎

评论
成就一亿技术人!
拼手气红包6.0元
还能输入1000个字符
 
红包 添加红包
表情包 插入表情
 条评论被折叠 查看
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值