I'm looking for a more elegant way to write my generic listener dispatcher class. I also hope that this might be useful to others.
The core of the problem is: I want the following code to work:
int main()
{
Foo a, b;
GenericDispatcher<Listener> dispatcher;
dispatcher.addListener(&a, "a");
dispatcher.addListener(&b, "b");
dispatcher.dispatch<&Listener::doThing>(6, 7); // #1
return 0;
}
I want dispatcher.dispatch<>() (#1) to be able to deduce the arguments and return type of the member function being passed as a template argument without me having to explicitly specify it.
I've had the need to write a lot of code to do with listeners with my latest project, and found I was re-writing a lot of those functions that handle registering/unregistering listeners, and dispatching events to listeners, so I had a go at a more generic approach.
Here's an example which works, but I'm forced to specify the return type and member function arguments when instantiating the dispatcher, meaning that I will only ever be able to dispatch stuff to functions with the same signature.
#include <iostream>
#include <string>
#include <map>
struct Listener
{
virtual void doThing(int, int) = 0;
virtual void doAnotherThing(int) = 0;
};
struct Foo : public Listener
{
virtual void doThing(int a, int b)
{
std::cout << a*b << std::endl;
}
virtual void doAnotherThing(int a)
{
std::cout << a << std::endl;
}
};
template <class LISTENER_CLASS, class RET_TYPE, class... PARAMS>
class GenericDispatcher
{
public:
void addListener(LISTENER_CLASS* listener, std::string name)
{
m_Listeners[name] = listener;
}
template <RET_TYPE (LISTENER_CLASS::*func)(PARAMS...)>
void dispatch(PARAMS... params)
{
for(auto it = m_Listeners.begin(); it != m_Listeners.end(); ++it)
(it->second->*func)(params...);
}
private:
std::map<std::string, LISTENER_CLASS*> m_Listeners;
};
int main()
{
Foo a, b;
GenericDispatcher<Listener, void, int, int> dispatcher; // This here sucks
dispatcher.addListener(&a, "a");
dispatcher.addListener(&b, "b");
dispatcher.dispatch<&Listener::doThing>(6, 7);
return 0;
}
What if I wanted to do this, for example?
int main()
{
Foo a, b;
GenericDispatcher<Listener, void, int, int> dispatcher;
dispatcher.addListener(&a, "a");
dispatcher.addListener(&b, "b");
dispatcher.dispatch<&Listener::doThing>(6, 7);
dispatcher.dispatch<&Listener::doAnotherThing>(6); // I want this to work as well
return 0;
}
What I need is to be able to turn this:
GenericDispatcher<Listener, void, int, int> dispatcher;
int this:
GenericDispatcher<Listener> dispatcher;