雪千渔 发表于 2021-5-30 11:34

C++中的事件分发器与委托

本帖最后由 雪千渔 于 2021-5-30 11:40 编辑

先看看如何使用的
#include "CoreLib/Events.hpp"
class EventTest
{
public:
    int cb_index;

    Action<int> e;
    void lambda_inst(int c) {
      std::cout << "lambda_inst callback" << std::endl;
    }
    void bind_inst(int c) {
      std::cout << "bind_inst callback" << std::endl;
    }
    static void static_method(int c) {
      std::cout << "static_method callback" << std::endl;
    }

    EventTest()
    {
      //静态lambda两种方式
      e.AddListener([](int c) { std::cout << "static lambda1 callback" << std::endl; });
      e += [](int c) { std::cout << "static lambda2 callback" << std::endl; };

      //静态函数
      e += static_method; //或 e.AddListener(static_method);
      //e -= static_method; 或 e.RemoveListener(static_method);

      //添加与移除闭包lambda方法,可以把lambda托管给this,然后最后按实例移除
      this->cb_index = e.AddListener(this, (int c) { this->lambda_inst(c); });
      //e.RemoveListenerByIndex(this->cb_index);

      //添加与移除成员方法
      e.AddListener(this, &EventTest::bind_inst);
      //e.RemoveListener(this, &EventTest::bind_inst);

      //执行
      e.Invoke(3);

      //移除实例中的所有事件
      e.RemoveByInstance(this);

      //移除全部事件
      e.RemoveAllListener();
    }
};

在程序中使用批量事件通知是很常用的场景,该事件分发器可以绑定多个静态与非静态函数,内部使用标准库的list和function实现,可变模板参数可以拓展到任意长度。
模仿C#中的委托,分为Events、Delegate、Action和Function。以下是继承的结构。
[*]Events (添加移除静态与实例事件)

[*]Delegate (执行事件、按实例移除或全部移除)

[*]Action (Delegate特化版本)
[*]Function (继承Delegate,实现带返回值列表的执行事件)
[*]Function<bool> (Function偏特化版本,实现返回值验证是否存在false)
[*]ActionEvents (Event特化版本)
[*]FunctionEvents (Event特化版本)

Events作为模板需要传入返回值与形参类型,同时Events作为基类,只能使用添加事件和移除事件,并不可以执行。而执行函数是在Delegate层以上的,因为ActionEvents和FunctionEvents只是Events的具体化,Action只是Delegate的具体化,所以就可以有以下的继承关系。
[*]ActionEvents (Events)

[*]Action (Delegate)
[*]FunctionEvents (Events)

[*]Delegate

[*]Function
[*]Function<bool>

Events类应该实现以下几个功能:
[*]添加与移除函数指针的事件(普通函数、成员静态函数与无捕获的lambda表达式)
[*]添加与移除实例事件(成员实例函数)
[*]通过索引来移除闭包lambda
[*]提供+=与-=运算符重载(仅函数指针类型)

Delegate类功能实现:
[*]Invoke执行所有事件
[*]按实例对象移除事件
[*]移除所有事件

普通的函数指针因为在内存中仅存在一份,所以在移除时可以方便的直接对保存的函数指针进行对比。
带捕获的lambda表达式可以直接转换成std::function类型,采用索引的方式来控制:将函数保存后,返回一个自增的索引,外部可以通过这个索引来移除。或者在创建时传入一个实例对象,最后按实例移除事件。
普通的实例函数就需要使用std::bind对实例进行绑定,但是绑定后就没办法对实例和成员指针进行判断,所以使用成员指针来比较,C++的成员指针必须要求指定的类型,所以使用类模板,并通过继承来做类型擦除,类型擦除后就可以将它们保存到一起,同时取值函数也是一个模板函数,在取值时对实例进行基类至子类的转换。
所有事件的绑定绑定都会返回一个索引值,用来标示该事件,主要用于没有名字函数的移除,在单独移除事件时需要该索引值,在不需要移除单事件或移除全部的情况下该索引可以丢弃。

该源码你可以在 https://github.com/Jayshonyves/JxCode.CoreLib/blob/main/CoreLib/Events.hpp 找到。
博客文章页:http://www.imxqy.com/code/cpp/events.html

#ifndef CORELIB_EVENTS_HPP
#define CORELIB_EVENTS_HPP

#include <list>
#include <functional>

template<typename TReturn, typename... TArgs>
class Events
{
public:
    using FunctionType = std::function<TReturn(TArgs...)>;
    using FunctionPointer = TReturn(*)(TArgs...);

protected:
    enum class FunctionInfoType
    {
      Static,
      Lambda,
      Member
    };
    class FunctionInfo
    {
    public:
      unsigned int index;
      FunctionInfoType type;
    public:
      FunctionInfo(const unsigned int& index, const FunctionInfoType& type)
            : index(index), type(type)
      {
      }
    public:
      bool operator ==(const FunctionInfo& r) {
            return this->index == r.index;
      }
      virtual TReturn Invoke(TArgs...) = 0;
    };
    class StaticFunctionInfo : public FunctionInfo
    {
    public:
      FunctionPointer ptr;
      StaticFunctionInfo(const unsigned int& index, FunctionPointer ptr)
            : FunctionInfo(index, FunctionInfoType::Static), ptr(ptr)
      {
      }
      virtual TReturn Invoke(TArgs... args) override {
            return ptr(args...);
      }
    };
    class LambdaFunctionInfo : public FunctionInfo
    {
    public:
      FunctionType func;
      void* instance;
      LambdaFunctionInfo(
            const unsigned int& index,
            void* instance,
            const FunctionType& func)
            : FunctionInfo(index, FunctionInfoType::Lambda), instance(instance), func(func)
      {
      }
      virtual TReturn Invoke(TArgs... args) override {
            return func(args...);
      }
    };
    template<typename TObj>
    class MemberFunctionInfo : public FunctionInfo
    {
    public:
      TObj* instance;
      TReturn(TObj::* ptr)(TArgs...);

      MemberFunctionInfo(
            const unsigned int& index,
            TObj* instance,
            TReturn(TObj::* ptr)(TArgs...))
            : FunctionInfo(index, FunctionInfoType::Member), instance(instance), ptr(ptr)
      {
      }
      virtual TReturn Invoke(TArgs... args) override {
            return (instance->*ptr)(args...);
      }
    };

protected:
    unsigned int index;
    std::list<FunctionInfo*> eventList;
public:
    int Count() const {
      return this->eventList.size();
    }
public:
    Events() : index(0) {}
    Events(const Events& right) = delete;
    Events(Events&& right) = delete;
    ~Events() {
      this->RemoveAllListener();
    }
protected:
    void RemoveAllListener() {
      for (auto it = this->eventList.begin(); it != this->eventList.end(); it++) {
            delete* it;
      }
      this->eventList.clear();
    }
public:
    //static
    unsigned int AddListener(FunctionPointer funcPtr) {
      if (funcPtr == nullptr) {
            return 0;
      }
      this->eventList.push_back(new StaticFunctionInfo(++this->index, funcPtr));
      return this->index;
    }
    //member
    template<typename TObj>
    unsigned int AddListener(TObj* obj, TReturn(TObj::* ptr)(TArgs...)) {
      if (obj == nullptr) {
            return 0;
      }
      this->eventList.push_back(new MemberFunctionInfo<TObj>(++this->index, obj, ptr));
      return this->index;
    }
    //lambda
    template<typename TObj>
    unsigned int AddListener(TObj* obj, const FunctionType& func) {
      this->eventList.push_back(new LambdaFunctionInfo(++this->index, obj, func));
      return this->index;
    }

    //static
    unsigned int RemoveListener(FunctionPointer funcPtr) {
      for (auto it = this->eventList.begin(); it != this->eventList.end(); it++) {
            if ((*it)->type == FunctionInfoType::Static
                && static_cast<StaticFunctionInfo*>(*it)->ptr == funcPtr) {
                auto index = (*it)->index;
                delete* it;
                this->eventList.erase(it);
                return index;
            }
      }
      return 0;
    }
    //member
    template<typename TObj>
    unsigned int RemoveListener(TObj* obj, TReturn(TObj::* ptr)(TArgs...)) {
      for (auto it = this->eventList.begin(); it != this->eventList.end(); it++)
      {
            if ((*it)->type == FunctionInfoType::Member
                && static_cast<MemberFunctionInfo<TObj>*>(*it)->instance == obj
                && static_cast<MemberFunctionInfo<TObj>*>(*it)->ptr == ptr)
            {
                auto index = (*it)->index;
                delete* it;
                this->eventList.erase(it);
                return index;
            }
      }
      return 0;
    }


    unsigned int RemoveListenerByIndex(unsigned int index) {
      if (index <= 0) {
            return 0;
      }
      for (auto it = this->eventList.begin(); it != this->eventList.end(); it++) {
            if ((*it)->index == index) {
                delete* it;
                this->eventList.erase(it);
                return index;
            }
      }
      return 0;
    }

    unsigned int operator+=(FunctionPointer ptr) {
      return this->AddListener(ptr);
    }

    unsigned int operator-=(FunctionPointer ptr) {
      return this->RemoveListener(ptr);
    }
};

template<typename TReturn, typename... TArgs>
class Delegate : public Events<TReturn, TArgs...>
{
    usingbase = Events<TReturn, TArgs...>;
public:
    void Invoke(TArgs... t) {
      for (auto& item : this->eventList) {
            item->Invoke(t...);
      }
    }

    void RemoveAllListener() {
      base::RemoveAllListener();
    }

    //member lambda
    template<typename TObj>
    void RemoveByInstance(TObj* obj) {
      using MemberFunInfo = typename Events<TReturn, TArgs...>::template MemberFunctionInfo<TObj>;
      using LambdaFunInfo = typename Events<TReturn, TArgs...>::LambdaFunctionInfo;

      for (auto it = this->eventList.begin(); it != this->eventList.end(); ) {
            if ((
                (*it)->type == base::FunctionInfoType::Member
                && (static_cast<MemberFunInfo*>(*it))->instance == obj
                ) || (
                (*it)->type == base::FunctionInfoType::Lambda
                && static_cast<LambdaFunInfo*>(*it)->instance == obj
                )
                )
            {
                delete* it;
                it = this->eventList.erase(it);
            }
            else {
                it++;
            }
      }
    }
};

template<typename... TArgs>
using ActionEvents = Events<void, TArgs...>;

template<typename... TArgs>
using Action = Delegate<void, TArgs...>;

template<typename TReturn, typename... TArgs>
using FunctionEvents = Events<TReturn, TArgs...>;

template<typename TReturn, typename... TArgs>
class Function : public Delegate<TReturn, TArgs...>
{
public:
    std::vector<TReturn> InvokeResult(TArgs... args) {
      std::vector<TReturn> retList;
      for (auto& item : this->eventList) {
            retList.push_back(item->Invoke(args...));
      }
      return retList;
    }
};

template<>
class Function<bool> : public Delegate<bool>
{
public:
    std::vector<bool> InvokeResult() {
      std::vector<bool> retList;
      for (auto& item : this->eventList) {
            retList.push_back(item->Invoke());
      }
      return retList;
    }

    bool IsValidReturnInvoke() {
      for (const bool& item : this->InvokeResult()) {
            if (!item) return false;
      }
      return true;
    }
};


#endif // !CORELIB_EVENTS_HPP

sqzh 发表于 2021-5-30 14:58

学习技术,谢谢

nappywu 发表于 2021-6-22 22:59

6666,我们招人哦,要不要考虑下&#129325;

leepop 发表于 2021-6-24 16:30

发现了一个c++大佬,github库看起来很厉害,比较小白,有没有应用使用的场景,是不是可以辅助优化我的代码。

雪千渔 发表于 2021-7-1 19:32

leepop 发表于 2021-6-24 16:30
发现了一个c++大佬,github库看起来很厉害,比较小白,有没有应用使用的场景,是不是可以辅助优化我的代码 ...

就是一个基础型的库,给了个统一的Object,UTF8字符串,指针托管的内存管理方式,还有反射,做序列化比较方便。
页: [1]
查看完整版本: C++中的事件分发器与委托