Reputation: 405
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.
Upvotes: 3
Views: 15676
Reputation: 50540
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 instance
s 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 Dispatcher
s 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.
Upvotes: 4
Reputation: 342
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.
Upvotes: 2