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

#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, [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#中的委托,分为事件、Delegate、Action和Function。以下是继承的结构。
Events (添加移除静态与实例事件)
Delegate (执行事件、按实例移除或全部移除)
Action (Delegatet化版本)
Function (继承代表,实现带返回值列表的执行事件)
Function (Function偏特化版本,实现返回值验证是否存在false)
ActionEvents (Eventt化版本)
FunctionEvents (Eventt化版本)

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

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...>
{
    using  base = 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
  • 1
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论
### 回答1: Fanuc是一家全球领先的工业机人和自动化解决方案提供商,其产品包括各种型号的CNC机床控制和执行。CNC机床控制是通过Fanuc自主开发的C语言来编写控制程序的。 Fanuc的C语言执行可以通过编写程序来实现各种功能和操作。使用C语言编写的执行程序可以控制机床的各个轴向运动、刀具变速、切削参数调整等。通过编写执行程序,用户可以根据自己的需要灵活地控制机床的运动和工作过程。 Fanuc的C语言执行具有高度的可编程性和扩展性,用户可以根据自己的需求编写特定的功能模块,并通过调用这些模块来实现各种复杂的操作和功能。同时,Fanuc还提供了丰富的编程手册和示例代码,帮助用户快速学习和掌握C语言执行的编程技巧。 除了控制机床运动,Fanuc的C语言执行还可以与其他设备进行通信和数据交互。通过编写相应的通信模块,用户可以实现机床与工厂信息系统或其他设备的无缝连接,实现数据共享和协同工作。 总之,Fanuc的C语言执行是一种强大而灵活的工具,通过编写C语言程序,用户可以自主控制和定制机床的各种功能和操作,满足工业生产的不同需求。 ### 回答2: Fanuc C语言执行是指用C语言编写的程序,用于控制Fanuc机人执行特定任务。Fanuc机人是一种先进的工业机人,可以在自动化生产线上执行各种复杂的任务。 Fanuc C语言执行能够通过编写C语言程序来实现对机人的控制。通过使用Fanuc提供的API和函数库,程序员可以编写出适应各种生产任务的程序。这些程序可以包括机人的移动、操作和执行特定工作等功能。编写程序时,程序员通常会根据特定的任务需求,设定机人的运动轨迹、速度、力度等参数,以及执行任务的条件和逻辑。 Fanuc C语言执行的主要特点是灵活性和可定制性。由于使用C语言编写,程序员可以根据实际需求进行自由的编码和调试。同时,Fanuc C语言执行还提供了强大的调试和监控工具,帮助程序员快速定位问题并进行调整。此外,Fanuc还提供了丰富的文档和示例程序来帮助程序员学习和使用C语言执行。 总之,Fanuc C语言执行是一种强大的工具,可以帮助工业机人实现多样化的任务和操作。它提供了灵活性和可定制性,使得机人能够适应不同的生产需求,并提高生产线的效率和精度。 ### 回答3: Fanuc C语言执行是一种用于控制Fanuc机人的执行。Fanuc机人是一种工业机人,被广泛应用于自动化生产线。该执行是一种硬件设备,用于接收计算机上的C语言程序,并将其转化为机人的动作指令。 Fanuc C语言执行具有以下特点: 1. 高效性:C语言是一种高级编程语言,具有高效的执行能力。使用C语言编写的程序可以在执行上快速执行,提高生产线的工作效率。 2. 灵活性:Fanuc C语言执行可以接收不同功能和复杂度的C语言程序。使用C语言,可以实现各种各样的机人动作,如移动、抓取、放置、旋转等。用户可以根据实际需求编写自定义的程序,满足不同的自动化任务。 3. 可编程性:Fanuc C语言执行具有可编程性,可以用于开发复杂的控制算法和逻辑。通过编写C语言程序,可以实现机人与其他设备的联动、路径规划、力控制等功能,实现更加智能化和灵活化的生产过程。 4. 易于使用:Fanuc C语言执行提供友好的编程界面,使用户可以方便地编写和修改C语言程序。同时,Fanuc还提供了详细的文档和培训材料,帮助用户快速上手和掌握执行的使用方法。 总之,Fanuc C语言执行是一种功能强大、高效灵活的机人控制设备。它通过接收C语言程序,将各种动作指令传递给机人,实现自动化生产线的运行和控制。它的使用可以提高生产效率,实现更高水平的自动化生产。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

飞天的大鹅

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值