繁体   English   中英

C ++中的像C#一样的代表

[英]C#-Like Delegates in C++

我对此事做了一些研究,但还没有找到具体的解决方案。 我真的很想能够做到这一点:

    public delegate void VoidFloatCallback(float elapsedTime);
    public VoidFloatCallback OnEveryUpdate;
    public VoidFloatCallback OnNextUpdate;

    public virtual void Update(GameTime gameTime)
    {
        if (OnNextUpdate != null)
        {
            OnNextUpdate(gameTime);
            OnNextUpdate = null;
        }

        if (OnEveryUpdate != null)
        {
            OnEveryUpdate(gameTime);
        }

        this.OnUpdate(gameTime);
    }

但是,当然是在C ++中。 我发现只有一种解决方案可以为我提供这种功能。 但此后已离线,但我在此处将其重新发布到了http://codepad.org/WIVvFHv0 我发现的解决方案的唯一问题是,它不是现代的C ++ 11代码,并且缺少lambda支持。

我知道我可以用

    std::function

但是唯一的问题是它不支持运算符“ +=-=== ”。 现在,我已经考虑过制作自己的事件类,并进行

    vector<std::function>

带有一些模板,但是我发现std :: function没有实现operator ==因此我无法像C#那样使它看起来和感觉到。

无论如何,我的问题是这样的:

我想知道如何使用C ++ 11实现这样的事件系统-甚至可能。 甚至即使您知道实现支持多个侦听器的回调的更好/正确的方法(我也尽可能避免使用完整的Observer Pattern实现。)

更新#1

我对运营商的意图是:

    void some_func(float f) { /** do something with f **/ }
    void some_other_func(float f) { /** do something else with f **/ }
    OnNextUpdate += some_func();
    OnNextUpdate += some_other_func();

    OnNextUpdate(5.0f);
    // both some_func() and some_other_func() are called

    OnNextUpdate -= some_other_func();
    OnNextUpdate(5.0f);
    // only some_func() is called

C ++函数对象机制与C#方法完全不同。 特别是,功能对象基于值而不是引用。 在C ++中删除函数对象时可以识别它们的原因是,该函数对象具有标识,即,调用它们的对象和调用成员函数的对象。 同样,在C ++中,不可能一次直接获取对象和成员函数的地址。

为了使代表系统能够删除功能,您可以创建类似于std::function<Signature>但是要使用多个功能,并且要求每个使用的功能都必须是EqualityComparable 以下是这种委托系统的简单实现以及示例实现,这些实现用于成员函数的绑定器看起来像。 存在许多明显的扩展机会,因为此实现仅用作演示。

#include <algorithm>
#include <iostream>
#include <memory>
#include <utility>
#include <vector>

template <typename Signature>
struct delegate;

template <typename... Args>
struct delegate<void(Args...)>
{
    struct base {
        virtual ~base() {}
        virtual bool do_cmp(base* other) = 0;
        virtual void do_call(Args... args) = 0;
    };
    template <typename T>
    struct call: base {
        T d_callback;
        template <typename S>
        call(S&& callback): d_callback(std::forward<S>(callback)) {}

        bool do_cmp(base* other) {
            call<T>* tmp = dynamic_cast<call<T>*>(other);
            return tmp && this->d_callback == tmp->d_callback;
        }
        void do_call(Args... args) {
            return this->d_callback(std::forward<Args>(args)...);
        }
    };
    std::vector<std::unique_ptr<base>> d_callbacks;

    delegate(delegate const&) = delete;
    void operator=(delegate const&) = delete;
public:
    delegate() {}
    template <typename T>
    delegate& operator+= (T&& callback) {
        this->d_callbacks.emplace_back(new call<T>(std::forward<T>(callback)));
        return *this;
    }
    template <typename T>
    delegate& operator-= (T&& callback) {
        call<T> tmp(std::forward<T>(callback));
        auto it = std::remove_if(this->d_callbacks.begin(),
                                 this->d_callbacks.end(),
                                 [&](std::unique_ptr<base>& other) {
                                     return tmp.do_cmp(other.get());
                                 });
        this->d_callbacks.erase(it, this->d_callbacks.end());
        return *this;
    }

    void operator()(Args... args) {
        for (auto& callback: this->d_callbacks) {
            callback->do_call(args...);
        }
    }
};

// ----------------------------------------------------------------------------

template <typename RC, typename Class, typename... Args>
class member_call {
    Class* d_object;
    RC (Class::*d_member)(Args...);
public:
    member_call(Class* object, RC (Class::*member)(Args...))
        : d_object(object)
        , d_member(member) {
    }
    RC operator()(Args... args) {
        return (this->d_object->*this->d_member)(std::forward<Args>(args)...);
    }
    bool operator== (member_call const& other) const {
        return this->d_object == other.d_object
            && this->d_member == other.d_member;
    }
    bool operator!= (member_call const& other) const {
        return !(*this == other);
    }
};

template <typename RC, typename Class, typename... Args>
member_call<RC, Class, Args...> mem_call(Class& object,
                                         RC     (Class::*member)(Args...)) {
    return member_call<RC, Class, Args...>(&object, member);
}

// ----------------------------------------------------------------------------

void f(char const* str) { std::cout << "f(" << str << ")\n"; }
void g(char const* str) { std::cout << "g(" << str << ")\n"; }
void h(char const* str) { std::cout << "h(" << str << ")\n"; }

// ----------------------------------------------------------------------------

struct foo
{
    int d_id;
    explicit foo(int id): d_id(id) {}
    void bar(char const* str) {
        std::cout << "foo(" << this->d_id << ")::bar(" << str << ")\n";
    }
    void cbs(char const* str) {
        std::cout << "foo(" << this->d_id << ")::cbs(" << str << ")\n";
    }
};

// ----------------------------------------------------------------------------

int main()
{
    delegate<void(char const*)> d0;

    foo f0(0);
    foo f1(1);

    d0 += f;
    d0 += g;
    d0 += g;
    d0 += h;
    d0 += mem_call(f0, &foo::bar);
    d0 += mem_call(f0, &foo::cbs);
    d0 += mem_call(f1, &foo::bar);
    d0 += mem_call(f1, &foo::cbs);
    d0("first call");
    d0 -= g;
    d0 -= mem_call(f0, &foo::cbs);
    d0 -= mem_call(f1, &foo::bar);
    d0("second call");
}

那么boost.signals2呢?

boost::signals2::signal<void (float)> onEveryUpdate;
boost::signals2::signal<void (float)> onNextUpdate;

virtual void Update(float gameTime)
{
    onNextUpdate(gameTime);
    onNextUpdate.disconnect_all_slots();
    onEveryUpdate(gameTime);
} 

信号的connect功能基本上就是+=意思。

改为使用观察者模式呢?

class IVoidFloatCallback
{
public:
   virtual ~IVoidFloatCallback() { }
   virtual void VoidFloatCallback(float elapsedTime) = 0;
};

class Game
{
public:
    std::vector<IVoidFloatCallback*> mOnEveryUpdate;
    std::vector<IVoidFloatCallback*> mOnNextUpdate;

    void Update(float gameTime)
    {
       for ( auto& update : mOnNextUpdate )
       {
          update->VoidFloatCallback(gameTime);
       }
       mOnNextUpdate.clear();


       for ( auto& update : mOnEveryUpdate )
       {
           update->VoidFloatCallback(gameTime);
       }

       OnUpdate(gameTime);
    }
};

class UpdateMe : public IVoidFloatCallback
{
public:
   virtual void VoidFloatCallback(float elapsedTime) final
   {
     // Do something
   }
};

void InitGame()
{
    Game g;
    UpdateMe someThing;
    g.mOnEveryUpdate.push_back(&someThing);
    g.Update(1.0f);
}

我认为尝试使C ++看起来像C#并不是真正的“事情”,因为它与众不同。 我也会看一下有关多播的链接问题。

那里有许多提供这种东西的图书馆。 一些用于代表的呼叫操作符+ =,例如“ connect”或“ subscribe”。 例如boost.signal2poco AbstractEventlibsigc ++,或者如果您使用的是GUI Qt的插槽/信号 (或者如果您在c ++中使用gtk,则为c ++-gtk-utils Emitters )。

Poco库支持这样的代表,例如:

#include <iostream>
#include "Poco/Delegate.h"
#include "Poco/BasicEvent.h"

using namespace Poco;

struct Source {
    BasicEvent<int> theEvent;

    void fireEvent(int n) { theEvent(this, n); }
};

struct Target1 {
    void onEvent(const void * /*sender*/, int &arg) {
        std::cout << "onEvent from Target1: " << arg << std::endl;
    }
};

struct Target2 {
    void onEvent(const void * /*sender*/, int &arg) {
        std::cout << "onEvent from Target2: " << arg << std::endl;
    }
};

int main() {
    Source source;
    Target1 target1;
    Target2 target2;

    source.theEvent += delegate(&target1, &Target1::onEvent);
    source.theEvent += delegate(&target2, &Target2::onEvent);

    source.fireEvent(42);

    source.theEvent -= delegate(&target2, &Target2::onEvent);

    source.fireEvent(24);

    return 0;
}

输出:

来自Target1的onEvent:42

来自Target2的onEvent:42

来自Target1的onEvent:24

在库方面,我还建议您看一下Boost.Signals2。

暂无
暂无

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

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