Okay here's an interesting challenge for any C++ programmers out there.
I wanted to write a generic listener dispatcher class so I can implement the "observer" pattern without having to rewrite all of that stupid code for managing listeners.
This is what I've come up with:
#include <iostream>
#include <map>
template <class LISTENER_INTERFACE_CLASS>
class ListenerDispatcher
{
public:
void addListener(LISTENER_INTERFACE_CLASS* listener, std::string name)
{
m_Listeners[name] = listener;
}
template <class RET_TYPE, class... ARGS, class... PARAMS>
void dispatch(RET_TYPE (LISTENER_INTERFACE_CLASS::*func)(ARGS...), PARAMS&&... params) const
{
for(auto it : m_Listeners)
(it.second->*func)(params...);
}
private:
std::map<std::string, LISTENER_INTERFACE_CLASS*> m_Listeners;
};
Usage example:
struct ListenerInterface
{ virtual void thing(std::string text) = 0; };
struct SomeListener : public ListenerInterface
{ virtual void thing(std::string text)
{ std::cout << "SomeListener: " << text << std::endl; }};
struct AnotherListener : public ListenerInterface
{ virtual void thing(std::string text)
{ std::cout << "AnotherListener: " << text << std::endl; }};
int main()
{
ListenerDispatcher<ListenerInterface> dispatcher;
SomeListener l1;
AnotherListener l2;
dispatcher.addListener(&l1, "l1");
dispatcher.addListener(&l2, "l2");
dispatcher.dispatch(&ListenerInterface::thing, "hello");
return 0;
}
This works and is all fine and good, but it's not as optimal as it could be. Member function pointers are known at compile time, but I'm currently passing them as the first argument in
dispatch causing a minimal but unnecessary overhead.
The Challenge
Modify the ListenerDispatcher class so the following code:
dispatcher.dispatch(&ListenerInterface::thing, args);
Can be re-written as:
dispatcher.dispatch<&ListenerInterface::thing>(args);
I'm breaking my head over this, it's not as easy as you'd think it would be.