0

I am writing a very simple implementation of Observer pattern in C++. Because I want my Publisher to notify its subscribers with events that are different (e.g. not just a string, but a specific class), I have decided to use templates. My code is compiling fine, except that part that I don't know where to store all these Observers. If I use and std::list or std::vector, they will not allow to store specialized data, because their elements have to be the same. So my question is, how do I store all those observers in my Publisher class. Here is my code:

Observer.hpp
#ifndef H_OBSERVER
#define H_OBSERVER    

#include <memory>

class Publisher;

template <class T>
class Observer
{
    protected:
    virtual void Notify(std::shared_ptr<Publisher> source, T info) = 0;
};

#endif

Publisher.hpp
#ifndef H_PUBLISHER
#define H_PUBLISHER

#include "Observer.hpp"
#include <list>
#include <string>
#include <memory>

class Publisher
{

public:
    template<class T>
    void NotifyObservers();

    template <class T>
    void AddObserver(std::shared_ptr<Observer<T>> &obs);

    template <class T>
    void RemoveObserver(std::shared_ptr<Observer<T>> &obs);

protected:
    //std::list<std::shared_ptr<Observer> m_observers;
};

#endif
santahopar
  • 2,933
  • 2
  • 29
  • 50
  • I was thinking I could store all my observers in an std::list, like the one you see in the code - std::list m_observers. But that resulted in a compilation error. – santahopar Apr 18 '14 at 00:15
  • 2
    How about having all `Observer` inherit publicly from a base class like `ObserverBase` and publisher keep a `std::list`?Is that acceptable? – Arun Apr 18 '14 at 00:18
  • The problem is that when you have some data to send you need to know what Observer to inform. So it strikes me that perhaps you'd be better suited with something like the visitor pattern. – qeadz Apr 18 '14 at 00:19
  • @Arun, I can see cases where it can be used. But in my particular scenario it is the Observer class that is specialized. Because I want each observer to receive it's own notification. One of them can receive HardwareNotification, the other can receive ApplicationNotification, etc. And those can all be different classes, or even enums. – santahopar Apr 18 '14 at 00:20
  • @qeadz, could you please point me to an of implementation of the visitor pattern? a very simple one. – santahopar Apr 18 '14 at 00:21
  • usually observers use inheritance and virtual functions, not templates. Then, you just have to store pointers in the container, and call whatever virtual function you need to to notify your observers – Jules G.M. Apr 18 '14 at 00:22
  • 1
    Sure - here is one on StackOverflow even: http://stackoverflow.com/questions/2604169/could-someone-in-simple-terms-explain-to-me-the-visitor-patterns-purpose-with-e – qeadz Apr 18 '14 at 00:23
  • @Julius, when you mean pointers, you mean raw pointers? Like void*? – santahopar Apr 18 '14 at 00:23
  • I'd like to add that what I am suggesting here is if you have a set number of things your observers might care about, they only need implement an interface which has "accepts" for the types they want to consider. Then your Publisher is somewhat generic in that you can store a vector of pointers to Observers and when you have information to post, you can iterate over it and notify everyone. Each Observer would then process the information if it cared for it. – qeadz Apr 18 '14 at 00:25
  • Who owns the observers / the observable? – Deduplicator Apr 18 '14 at 00:26
  • You're missing a closing '>' which is a compilation error right there. – codah Apr 18 '14 at 00:29
  • @Deduplicator, the observers can be created in different parts of the code. Then they will have to subscribe to the publisher. – santahopar Apr 18 '14 at 00:29
  • because you have the line commented out – codah Apr 18 '14 at 00:30
  • @codah, thanks for pointing that out. But that's not the reason it was not working. – santahopar Apr 18 '14 at 00:31
  • @codah, just to be clear the error I get after I add that closing bracket ">": error C3857: 'Publisher::m_observers': multiple template parameter lists are not allowed – santahopar Apr 18 '14 at 00:32
  • No, I mean base class pointers to heap allocated derived class objects with virtual functions – Jules G.M. Apr 18 '14 at 00:32
  • Just pointing it out. You mentioned 'compilation error' and quoted invalid code, in your first comment.. – codah Apr 18 '14 at 00:33
  • @codah, OK, you're right. That ">" was missing. But the code WAS compiling with that line commented out. That declaration is wrong regardless of the missing ">" – santahopar Apr 18 '14 at 00:34
  • Can't you templatize the Publisher class? I see similar implementations in a quick Google search. [here](http://www.codeproject.com/Articles/3267/Implementing-a-Subject-Observer-pattern-with-templ) – codah Apr 18 '14 at 00:37
  • A simple, clean approach is to have a non-template Observer type that's passed all the available event information, then clients who want to can provide their own Adapter observers that report HardwareNotification or some enum or whatever else they want. No need for the observable object to buy into that at all. – Tony Delroy Apr 18 '14 at 00:38
  • Can you explain why `Observer` is templated on `T`? I see `T info`, but won't it be simpler if `info` is a an `enum`? – Arun Apr 18 '14 at 00:39
  • Please take a look at `std::function`. It does all you need coupled with a `std::weak_pointer`. – Deduplicator Apr 18 '14 at 00:39
  • @Deduplicator there's no evidence of a need for weak_pointer, given the existence of RemoveObserver implies deregistration before observer destruction. – Tony Delroy Apr 18 '14 at 00:42
  • @Tony: As the Observer gets a `std::shared_pointer`, it should either keep the observers alive or more likely remove dead observers itself, if no early deregistration was done. Otherwise, the API should use non-owning raw pointers. – Deduplicator Apr 18 '14 at 00:47
  • @Deduplicator as written, it will keep them alive until deregistered; the choice is not one of extra safety in removing dead observers, it's a question of whether the observer itself puts them on life support, which implies either the observer itself, or something with a weak pointer to it, will end up doing any deregistration. All legitimate models for different needs and nice to enumerate, but orthogonal to the different-notification-types thrust of the question. – Tony Delroy Apr 18 '14 at 01:20
  • @Tony: As written, it will do nothing, as the handling and storing code is not yet in evidence, nor did the OP say. My comment still stands, you did not refute anything. (BTW: The OP later in his own answer stores weak_pointers) – Deduplicator Apr 18 '14 at 01:24
  • @Deduplicator "as written" referred to the AddObserver parameter and commented data member indicating what he was trying to do. That he's changed isn't surprising, his "answer" doesn't meet his stated needs and you've advised him to change. I'm not refuting anything - just pointing out that you've leapt on a legitimate alternative functional model without any particular evidence that it was any more appropriate to the OP's intended use - that's not to say it's not more appropriate, just that it's good to separate issues for clarity. All this rambling commentary -> OP still confused. – Tony Delroy Apr 18 '14 at 01:31

2 Answers2

0

Save your observers in a standard container like std::vector.

For lifetime management and identification, use: std::weak_pointer,
for polymorphic function (objects), std::function,
and to put them together use std::pair.

That means you can scrap your non-standard Observer.

Deduplicator
  • 44,692
  • 7
  • 66
  • 118
0

The initial solution I posted was not correct. It's because I am new to templates and did not know how to effectively utilize them. The solution I am posting now is a working one:

//IObserver.hpp
#pragma once
#include <memory>

template <class T>
class IObserver
{
public:
    virtual ~IObserver() {};
    virtual void Notify(T data) = 0;
protected:
};



//Observable.hpp
#pragma once

#include "IObserver.hpp"
#include <list>
#include <string>
#include <memory>

template<class T>
class Observable
{
public:
    void NotifyObservers(T data)
    {
        for (auto o : m_observers)
        {
            o.Notify(data);
        }
    }

    void AddObserver(std::shared_ptr<IObserver<T>> &obs)
    {
        m_observers.push_back(obs);
    }

    void RemoveObserver(std::shared_ptr<IObserver<T>> &obs)
    {
        m_observers.remove(obs);
    }

private:
    std::list<std::shared_ptr<IObserver<T>>> m_observers;
};
santahopar
  • 2,933
  • 2
  • 29
  • 50