I am in the process of writing a template class that would implement a Listener interface for every component that would need it within my application. I want a component to be able to listen to changes to an individual field within a listened object. The fields of each object are represented by an enum, and I've got a simple Notifier/Publisher that works great as such:
template <typename EFieldsEnum>
class AbstractNotifier
{
public:
void addListener(EFieldsEnum listenedField, INotifierListener<EFieldsEnum>* listener)
{
if (_listeners.find(listenedField) == _listeners.cend())
{
std::unordered_set<INotifierListener<EFieldsEnum>*> listeners;
listeners.insert(listener);
_listeners.emplace(listenedField, listeners);
}
else
{
_listeners.find(listenedField)->second.insert(listener);
}
}
void notify(EFieldsEnum updatedField)
{
for (INotifierListener<EFieldsEnum>* listener : _listeners[updatedField])
{
listener->onUpdate(updatedField);
}
}
protected:
std::unordered_map<EFieldsEnum, std::unordered_set<INotifierListener<EFieldsEnum>*>> _listeners;
};
I am trying to change the method signature slightly, in order for each listening component to register by passing a void() method that would be called as a callback, rather than implement the onUpdate generic method followed by a switch case. Ideally, I would want my listening components (say, Component1) to register without having to define a specific Listener interface for each type of Notifier that I will have, as such :
notifier.addListener(FieldEnum::MyField1, this, &Component1::onMyField1Updated);
and then Component2 could also register as such :
notifier.addListener(FieldEnum::MyField1, this, &Component2::someOtherMethod);
without Component1 and Component2 needing to implement a common interface. Would this be possible? I am struggling to figure out what type is expected to generically represent the member method that is passed through :
class IListener {};
void (IListener::*ListenerCallback)();
template <typename EFieldsEnum>
class AbstractNotifier
{
public:
void addListener(EFieldsEnum listenedField, IListener* listener, ListenerCallback listenerCallback)
{
std::pair<IListener*, ListenerCallback> pairToAdd = std::make_pair<IListener*, ListenerCallback>(listener, ListenerCallback);
if (_listeners.find(listenedField) == _listeners.cend())
{
std::unordered_set<std::pair<IListener*, ListenerCallback>> listenersAndCallbacks;
listenersAndCallbacks.insert(pairToAdd);
_listeners.emplace(listenedField, listenersAndCallbacks);
}
else
{
_listeners.find(listenedField)->second.insert(pairToAdd);
}
}
void notify(EFieldsEnum updatedField)
{
for (const std::pair<IListener*, ListenerCallback>& listenerAndCallback : _listeners[updatedField])
{
IListener* listener = listenerAndCallback.first;
ListenerCallback listenerCallback = listenerAndCallback.second;
listener->*listenerCallback();
}
}
protected:
std::unordered_map<EFieldsEnum, std::unordered_set<std::pair<IListener*, ListenerCallback>>> _listeners;
};