3

I am designing a simple event system for my game engine. I want to implement the following event dispatcher interface:

// Create event dispatcher.
Dispatcher dispatcher;

// Create objects b and c.
// Created objects extend base A class.
A* b = new B();
A* c = new C();

// Register b and c in dispatcher.
// I guess I should also pass member function pointer,
// but I am not sure how to do it.
// Like this; dispatcher.Add(b, &B::DoWork) ?
// Member function that I want to accept is always bool func(/*no params*/)
dispatcher.Add(b /*, member function pointer?*/);
dispatcher.Add(c /*, member function pointer?*/);

// Trigger passed functions for registered objects.
dispatcher.Trigger();

I guess, Im gonna need some kind of wrapper struct inside Dispatcher, to hold [pObject, pFunc] pair.

struct Wrapper
{
   A* pObj; // Of base class A.
   // ??    // Pointer to given member function of A.
};

I dont know how to implement member function pointer logic. As I need it for a real time system, I would like prefer a relatively fast solution. I appreciate all suggestions.

I would prefer to avoid 3rd party libraries.

Pharap
  • 3,826
  • 5
  • 37
  • 51
DannyX
  • 395
  • 1
  • 5
  • 11
  • 3
    Look at [std::function](http://en.cppreference.com/w/cpp/utility/functional/function). – Mohamad Elghawi Dec 08 '15 at 11:08
  • 3
    [This](http://blog.molecular-matters.com/2011/09/19/generic-type-safe-delegates-and-events-in-c/) one also can be a good point from which to start. – skypjack Dec 08 '15 at 11:08

2 Answers2

4

According with the comments, you can use either std::function or a structure similar to the one proposed here.
It follows a minimal and working example of the second suggestion:

class Dispatcher {
    Dispatcher() { }

    template<class C, void(C::*M)() = C::receive>
    static void invoke(void *instance) {
        (static_cast<C*>(instance)->*M)();
    }

public:
    template<class C, void(C::*M)() = &C::receive>
    static Dispatcher create(C *instance) {
        Dispatcher d;
        d.fn = &invoke<C, M>;
        d.instance = instance;
        return d;
    }

    void operator()() {
        (fn)(instance);
    }

private:
    using Fn = void(*)(void *);
    Fn fn;
    void *instance;
};

struct S {
    void receive() { }
};

int main() {
    S s;
    Dispatcher d = Dispatcher::create(&s);
    d();
};

Note that it has to be modified accordingly with your requirements (currently, the targeted member method has no return value and does not accept arguments).
Also, it can be easily extended to store an array of target member methods, that is what you were looking for: simply store a vector of pairs of instances and invoke functions.
In addition, you can use variadic template with the Dispatcher function to let it be more flexible. This way, you'll be able to define Dispatchers having different return types and arguments lists.

EDIT

It follows an example of a Dispatcher that accepts more than one target member function.
To extend it by means of variadic template as above mentioned is still quite easy.

#include <vector>
#include <utility>

class Dispatcher { 
    template<class C, void(C::*M)() = C::receive>
    static void invoke(void *instance) {
        (static_cast<C*>(instance)->*M)();
    }

public:
    template<class C, void(C::*M)() = &C::receive>
    void bind(C *instance) {
        auto pair = std::make_pair(&invoke<C, M>, instance);
        targets.push_back(pair);
    }

    void operator()() {
        for(auto pair: targets) {
            (pair.first)(pair.second);
        }
    }

private:
    using Fn = void(*)(void *);
    std::vector<std::pair<Fn, void*>> targets;
};

struct S {
    void receive() { }
};

struct T {
    void receive() { }
};

int main() {
    S s;
    T t;
    Dispatcher d;
    d.bind(&s);
    d.bind(&t);
    d();
};

Please note that in the examples above there is no guarantee that the pointer submitted as instance is still valid once actually used. You should either take care of the lifetime of your bound objects or slightly modify the example in order to introduce a more safer handle.

skypjack
  • 49,335
  • 19
  • 95
  • 187
2

If you can structure your A-derived classes appropriately so that you can have a process() method, you could simply use polymorphic dispatch.

Something like:

struct A
{
  virtual void process() = 0;
  virtual ~A() {}
}
struct B : public A
{
  virtual void process() { /* process event B... */ }
}
struct C : public A
{
  virtual void process() { /* process event C... */ }
}

The trigger method of your dispatcher would just have a loop for all registered objects calling the process() method on the pointer and let polymorphism call the appropriate method.

eldruin
  • 332
  • 3
  • 16
  • 1
    This solution is the first one that comes to mind, but It assumes that we know all possible events. My API needs flexibility, that provides posibility for user to create his own dispatcher (his own event) and to easly register all participants. – DannyX Dec 08 '15 at 11:32