I’ve recently had to implement a dispatch system for a side project of mine. I found a very useful link in implementing the common Observer pattern in C++11 and modified it a bit to make use of variadic templates and also to add the ability to remove listeners.
#include <functional> #include <map> #include <vector> #include <utility> template <typename Event> struct FunctionInfo { Event m_event; unsigned int m_vectorIndex; }; template <typename Event, typename... Args> class Observable { public: Observable() = default; virtual ~Observable() = default; template <typename Observer> const FunctionInfo<Event> Register(const Event &event, Observer &&observer) { m_observers[event].push_back(std::forward<Observer>(observer)); FunctionInfo<Event> FunctionInfo{ event, m_observers[event].size() - 1 }; return FunctionInfo; } template <typename Observer> const FunctionInfo<Event> Register(const Event &&event, Observer &&observer) { m_observers[std::move(event)].push_back(std::forward<Observer>(observer)); FunctionInfo<Event> FunctionInfo{ event, m_observers[event].size() - 1 }; return FunctionInfo; } template <typename... Parameters> void Notify(const Event &event, Parameters... Params) const { if (m_observers.size() > 0) { for (const auto &observer : m_observers.at(event)) { observer(Params...); } } } const bool Remove(const FunctionInfo<Event> &functionInfo) { auto callbackVectorIter = m_observers.find(functionInfo.m_event); if (callbackVectorIter != m_observers.end()) { auto callbackVectors = m_observers[functionInfo.m_event]; auto callbackRemove = callbackVectors.begin() + functionInfo.m_vectorIndex; callbackVectors.erase(callbackRemove); m_observers[functionInfo.m_event] = callbackVectors; return true; } return false; } Observable(const Observable &) = delete; Observable &operator=(const Observable &) = delete; private: std::map<Event, std::vector<std::function<void(Args...)>>> m_observers; }; |
Sample usage is below:
#include <iostream> enum class EventTypes { EventTypeFirst = 0, EventTypeSecond }; struct DispatchArgs { int i; }; class DispatcherNoArgs : public Observable<EventTypes> { public: DispatcherNoArgs() = default; ~DispatcherNoArgs() = default; void Send() { Notify(EventTypes::EventTypeFirst); } }; class DispatcherArgs : public Observable<EventTypes, DispatchArgs &> { public: DispatcherArgs() = default; ~DispatcherArgs() = default; void Send() { DispatchArgs args{ 123 }; Notify(EventTypes::EventTypeSecond, args); } }; void DispatchNoArgsReceiver() { std::cout << "DispatchNoArgsReceiver (function) called " << std::endl; } void DispatchArgsReceiver(DispatchArgs &args) { std::cout << "DispatchArgsReceiver called -- args: " << args.i << std::endl; } int main() { DispatcherNoArgs noArgs; auto functionInfo1 = noArgs.Register(EventTypes::EventTypeFirst, DispatchNoArgsReceiver); auto functionInfo2 = noArgs.Register(EventTypes::EventTypeFirst, [] { std::cout << "DispatchNoArgsReceiver (lambda) called " << std::endl; }); noArgs.Send(); //Calls DispatchNoArgsReceiver and lambda noArgs.Remove(functionInfo1); noArgs.Send(); //Calls just lambda DispatcherArgs args; auto functionInfo3 = args.Register(EventTypes::EventTypeSecond, std::bind(DispatchArgsReceiver, std::placeholders::_1)); args.Send(); //Calls bound function return 0; } |
Hi Admin
I am just a learner (saying so might save me from embarrassment). Thanks for your wonderful code that allows me to learn C++11 and Observer Pattern).
I am still curious as to what the member m_vectorIndex of struct FunctionInfo does (perhaps, something to do with “dispatching”), as it seems to never get updated.
Regards
Hung
Comment by Hung — February 8, 2018 @ 11:00 PM