C#-Like Delegates in C++

c#, c++, c++11

Solution

The C++ function object mechanism is quite different from the C# approach. In particular, function objects are based on values rather than on references. The reason function objects can be identified when removing them in C++ is that the function object have an identity, i.e., the object they are called on and the member function being called. Also, in C++ it isn't possible to directly take the address of an object and a member function at once.

To make a system of delegates work which allows removal of functions, you could create something similar to `std::function<Signature>` but using multiple functions and requiring that each of the used functions is `EqualityComparable`. Below is a simple implementation of such a delegate system together with an example implementation how a binder for member functions could look like. There are many obvious extension opportunities as this implementation is only intended as a demo.

#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");
}

Problem

I have done a bit of research on the matter but have not come to a concrete solution. I would really like to be able to do this: ``` 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); } ``` But in C++ of course. I have found only one solution that provides me with such a feature; but has since been taken offline but I reposted it here http://codepad.org/WIVvFHv0. The only issue with the solution I have found is that it isn't modern C++11 code and lacks lambda support. I know that I can use ``` std::function ``` but the only issue with that is it does not support the operators "`+=`, `-=`, `==`". Now I have thought about making my own Events class and having a ``` vector<std::function> ``` with some templating but I found out that std::function does not implement the operator `==` so I couldn't make it look and feel like C# does. Anyway, my question is this: I would to know how I can implement such an event system using C++11 -- or if it is even possible. Or even if you know of a better/proper way to implement Callbacks that support multiple listeners (I'd like to avoid a full blown Observer Pattern implementation if at all possible.) Update #1 My intentions for the operators were this: ``` 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 ```

Original source

Related problems