Home > Programming > C++11-style Observer

C++11-style Observer

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;
}
Categories: Programming Tags:
  1. Hung
    February 8th, 2018 at 23:00 | #1

    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

  1. No trackbacks yet.