简体   繁体   English

尝试使用 std::functional 和 std::bind 在 C++ 中创建 C# 样式的事件访问修饰符

[英]Try to create C# style event access modifier in C++ using std::functional and std::bind

I'm trying to create C# style event handling, ie expose += operator for everybody and expose Invoke method only for the containing class.我正在尝试创建 C# 样式的事件处理,即为所有人公开 += 运算符,并仅为包含类公开Invoke方法。

I'm using std::functional and std::bind as explained here to create callback mechanism: C++ callback using class member我正在使用 std::functional 和 std::bind 来创建回调机制: C++ callback using class member

To enable invoking the event only from containing class, I created Event::Invoke() method private, and then I create a friend class of Event called ClassWithEvent thats has protected method InvokeEvent which calling Event::Invoke .为了仅从包含类调用事件,我创建了Event::Invoke()方法私有,然后我创建了一个名为ClassWithEventEvent朋友类,它具有调用Event::Invoke保护方法InvokeEvent All the inheritanced classes of ClassWithEvent can invoke the event using base class InvokeEvent method. ClassWithEvent所有继承类ClassWithEvent可以使用基类InvokeEvent方法调用事件。

Additionly, I want to enable events to have different kind of args, so I create base class EventArgs which can be extended by other king of args.此外,我想让事件具有不同类型的 args,所以我创建了基类EventArgs ,它可以被其他 args 之王扩展。

#include <iostream>
#include <vector>
#include <functional>

using namespace std;
using namespace placeholders;

class EventArgs
{
};

class Event
{
    friend class ClassWithEvent;

public:
    void operator+=(function<void(const EventArgs &)> callback)
    {
        m_funcsList.push_back(callback);
    }

protected:
    void Invoke(const EventArgs & args) const
    {
        for (function<void(const EventArgs &)> func : m_funcsList)
        {
            func(args);
        }
    }

    vector<function<void(const EventArgs &)>> m_funcsList;
};

class ClassWithEvent
{
protected:
    void InvokeEvent(const Event & event, const EventArgs & args)
    {
        event.Invoke(args);
    }
};

For exaple, I have ApplesTree class with AppleFallEvent event, I have ApplesCollector class which should be notified about falling apples:对于的exaple,我有ApplesTree带班AppleFallEvent事件,我有ApplesCollector应当通知有关掉落的苹果类:

class AppleFallEventArgs : public EventArgs
{
public:
    int size;
    int weight;
};

class ApplesTree : public ClassWithEvent
{
public:
    Event AppleFallEvent;

    void triggerAppleFall(int size, int weight)
    {
        AppleFallEventArgs args;

        args.size = size;
        args.weight = weight;

        ClassWithEvent::InvokeEvent(AppleFallEvent, args);
    }
};

class ApplesCollector
{
public:
    void HandleAppleFallEvent(const AppleFallEventArgs & args)
    {
        cout << "Apple size is " << args.size << "weight is " << args.weight << endl;
    }
};

int main(int argc, char* argv[])
{
    ApplesTree applesTree;
    ApplesCollector applesCollector;

    applesTree.AppleFallEvent += bind(&ApplesCollector::HandleAppleFallEvent, &applesCollector, _1);

    applesTree.triggerAppleFall(1, 2);

    return 0;
}

Well, I try to compile this and get the following errors:好吧,我尝试编译它并收到以下错误:

C2672 'std::invoke': no matching overloaded function found C2672“std::invoke”:找不到匹配的重载函数

and

C2893 Failed to specialize function template 'unknown-type std::invoke(_Callable &&,_Types &&...)' C2893 未能专门化函数模板“未知类型 std::invoke(_Callable &&,_Types &&...)”

I can't look out where is the problem because this errors belongs to std code and its really hard to deal with it.我看不出问题出在哪里,因为这个错误属于标准代码,很难处理。 Any way, I found that if I removing the usage of Event::operator+=() the compile is successfull.无论如何,我发现如果我删除Event::operator+=()的使用,则编译成功。

Can someone point out what is the problem here?有人可以指出这里的问题是什么吗?

Thanks!谢谢!

You need to have Event know which particular args it will use.您需要让Event知道它将使用哪些特定的参数。 Note that using namespace std;注意using namespace std; is a bad idea.是个坏主意。 I've also replaced the bind with a lambda.我还用 lambda 替换了bind

// no empty class EventArgs

template<typename EventArgs>
class Event
{
    friend class ClassWithEvent;

public:
    void operator+=(std::function<void(const EventArgs &)> callback)
    {
        m_funcsList.emplace_back(std::move(callback));
    }

private:
    void Invoke(const EventArgs & args) const
    {
        for (auto & func : m_funcsList)
        {
            func(args);
        }
    }

    std::vector<std::function<void(const EventArgs &)>> m_funcsList;
};

class ClassWithEvent
{
protected:
    template<typename EventArgs>
    void InvokeEvent(const Event<EventArgs> & event, const EventArgs & args)
    {
        event.Invoke(args);
    }
};

class AppleFallEventArgs
{
public:
    int size;
    int weight;
};

class ApplesTree : public ClassWithEvent
{
public:
    Event<AppleFallEventArgs> AppleFallEvent;

    void triggerAppleFall(int size, int weight)
    {
        AppleFallEventArgs args;

        args.size = size;
        args.weight = weight;

        InvokeEvent(AppleFallEvent, args);
    }
};

class ApplesCollector
{
public:
    void HandleAppleFallEvent(const AppleFallEventArgs & args)
    {
        std::cout << "Apple size is " << args.size << "weight is " << args.weight << std::endl;
    }
};

int main(int argc, char* argv[])
{
    ApplesTree applesTree;
    ApplesCollector applesCollector;

    applesTree.AppleFallEvent += [&](auto & args){ applesCollector.HandleAppleFallEvent(args); };

    applesTree.triggerAppleFall(1, 2);

    return 0;
}

The problem here is that event handler method that you are trying to bind does not match event handler function because HandleAppleFallEvent takes const AppleFallEventArgs & instead of const EventArgs & .这里的问题是您尝试绑定的事件处理程序方法与事件处理程序函数不匹配,因为HandleAppleFallEvent采用const AppleFallEventArgs &而不是const EventArgs &

Also there are a lot of unnecessary copying, for example for (function<void(const EventArgs &)> func : m_funcsList) can be replaced with for (function<void(const EventArgs &)> & func : m_funcsList) .还有很多不必要的复制,例如for (function<void(const EventArgs &)> func : m_funcsList)可以替换for (function<void(const EventArgs &)> & func : m_funcsList)

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM