Using smart pointer instead of raw pointer for Observer design pattern - c++

I have an implementation for Observer design pattern where Observer stores a list of Subject and vice versa. So in case a Observer/Subject goes out of scope, the corresponding object is deleted in the destructor and no dangling reference remains stored.
Thing is raw pointers are rarely used in modern C++ and I'd want to replace them with a smart pointer.
My understanding is the utility shouldn't be taking ownership of the objects so std::weak_ptr makes more sense to me. Does that sound fair?
If so, changing the underlying data structure to hold weak_ptr of Subject would make sense, yes?
std::unordered_set<std::weak_ptr<Subject<T>>, std::hash<std::weak_ptr<Subject<T>>> _subjects;
I am unsure of whether there may be some corner cases where storing weak_ptr in an std::unordered_set could cause problems.
Running into compile errors related to the hash but below is the link to the sample code with weak_ptr.
Code with weak_ptr (though it doesn't compile)
Original sample code with raw pointer (shown below)
template<typename T>
class Subject;
template<typename T>
class Observer
{
std::set<Subject<T>*> _subjects; // TODO: change to smart pointer
public:
virtual void update(T val) = 0;
void addSubject(Subject<T>* subject)
{
printf ("[Observer] Adding Subject to observer - ");
_subjects.insert(subject);
std::cout << "number of Subjects = " << _subjects.size() << "\n\n";
}
void removeSubject(Subject<T>* subject)
{
printf ("[Observer] Removing Subject from observer - ");
_subjects.erase(subject);
std::cout << "number of Subjects = " << _subjects.size() << "\n";
}
size_t getNumberOfSubjects()
{
return _subjects.size();
}
void doesExist(Subject<T>* sub)
{
auto it = _subjects.find(sub);
std::cout << "[Observer] Subject Exists = " << (it != _subjects.end()) << std::endl;
}
virtual ~Observer()
{
while (!_subjects.empty())
{
(*_subjects.begin())->detach(this);
}
}
};
template<typename T>
class Subject
{
std::set<Observer<T>*> _observers; // TODO: change to smart pointer
protected:
Subject() = default;
public:
void attach(Observer<T>* observer)
{
printf ("~~ [Subject] Attaching observer ~~\n");
_observers.insert(observer);
observer->addSubject(this);
}
void detach(Observer<T>* observer)
{
printf ("[Subject] Detaching observer ~ ");
std::cout << "observers size = " << _observers.size() << "\n";
_observers.erase(observer);
observer->removeSubject(this);
}
void notify(const T& val)
{
for (auto& observer : _observers)
{
observer->update(val);
}
}
size_t getNumberOfObservers()
{
return _observers.size();
}
virtual ~Subject()
{
printf ("\n~Subject\n");
printf ("Observer size = %ld\n", _observers.size());
for (auto& obs : _observers)
{
obs->removeSubject(this);
}
}
};
template<typename T>
class ConcreteSubject : public Subject<T>
{
T _value;
public:
void set(const T& value)
{
_value = value;
this->notify(value);
}
};
template<typename T>
class ConcreteObserver : public Observer<T>
{
public:
void update(T value)
{
std::cout << "Observer Notified: " << value << "\n";
}
};
int main()
{
ConcreteObserver<int> obs;
ConcreteSubject<int> sub;
sub.attach(&obs);
sub.set(5);
printf ("\n~~> Before detaching:\nnumber of subjects in Observer = %ld ~~\n", obs.getNumberOfSubjects());
printf ("number of observers in Subject = %ld\n\n", sub.getNumberOfObservers());
sub.detach(&obs);
printf ("\n~~> After detaching:\nnumber of subjects in Observer = %ld ~~\n", obs.getNumberOfSubjects());
printf ("number of observers in Subject = %ld\n", sub.getNumberOfObservers());
// subject no longer exists in observer
obs.doesExist(&sub);
return 0;
}

Related

How to manage different types of data in the base class?

My goal is to separate data from various implementations. I don't want my things to know what actual subclass it is they are working with, either way around. To make things perform only a single task with minimal information.
I'll throw some code in your eyes first.
// Example program
#include <iostream>
#include <string>
#include <memory>
#include <vector>
#include <functional>
class Model
{
public:
virtual bool set(int p_attrId, int p_value) {return false;};
virtual bool get(int p_attrId, int & p_value) const {return false;};
};
class Derived: public Model
{
static constexpr int c_classId = 1;
int value = 1;
public:
enum EAttrs
{
eAttr1 = c_classId * 1000
};
virtual bool set(int p_attrId, int p_value) override
{
switch(p_attrId)
{
case eAttr1:
value = p_value;
return true;
default:
return Model::set(p_attrId, p_value);
}
}
virtual bool get(int p_attrId, int & p_value) const override
{
switch(p_attrId)
{
case eAttr1:
p_value = value;
return true;
default:
return Model::get(p_attrId, p_value);
}
}
};
// GuiTextBoxComponent.h
// no includes to any class derived from model
class GuiTextBoxComponent
{
std::weak_ptr<Model> m_model;
int m_attrId;
public:
void draw()
{
auto locked = m_model.lock();
if(locked)
{
int value;
bool result = locked->get(m_attrId, value);
if(!result)
{
std::cout << "Failed to get attribute " << m_attrId << "\n";
return;
}
std::cout << "AttrID: " << m_attrId << " Value: " << value << "\n";
}
else
{
std::cout << "Model is dead\n";
}
}
void setSource(std::weak_ptr<Model> p_model, int p_attrId)
{
m_model = p_model;
m_attrId = p_attrId;
}
};
int main()
{
std::shared_ptr<Model> model (new Derived);
GuiTextBoxComponent textbox;
textbox.setSource(model, Derived::eAttr1);
textbox.draw();
}
The motivation behind this is acquisition of all data from a single interface.
I need to be able to add functionality like the GuiTextBoxComponent, without #include "Derived1.h" in its header.
The challenge with this design is that the Model interface needs to implement all types required from anywhere in the program.
How would you extend the types provided?
Is there some other design that could be used to achieve similar results?
Generally, I think this is an XY problem but here is how you can beautify your code a bit. First, I implemented two interfaces: Getter and Setter like:
enum class EAttrs {
eAttr1
};
template <typename GetterImpl>
struct Getter {
bool get(EAttrs const attrId, int& value) {
switch (attrId) {
case EAttrs::eAttr1:
return static_cast<GetterImpl*>(this)->get(value);
default:
return false;
}
}
};
template <typename SetterImpl>
struct Setter {
bool set(EAttrs const attrId, int value) {
switch (attrId) {
case EAttrs::eAttr1:
return static_cast<SetterImpl*>(this)->set(value);
default:
return false;
}
}
};
Here I used CRTP, i.e. static polymorphism. Then implementation of your derived classes is a bit simpler:
class Derived1 : public Getter<Derived1>, Setter<Derived1> {
int value = 1;
public:
bool set(int p_value) {
value = p_value;
return true;
}
bool get(int & p_value) {
p_value = value;
return true;
}
};
class Derived2 : public Getter<Derived1>, Setter<Derived1> {
int value = 2;
public:
bool set(int p_value) {
value = p_value;
return true;
}
bool get(int & p_value) {
p_value = value;
return true;
}
};
Finally, since we were using CRTP, there is no need for creating std::unique_ptr. Code that's using above classes could look like:
template <typename T>
void printInt(Getter<T>& model, EAttrs p_attrId) {
int value;
bool result = model.get(p_attrId, value);
if (!result)
{
std::cout << "Failed to get attribute " << static_cast<int>(p_attrId) << "\n";
return;
}
std::cout << "AttrID: " << static_cast<int>(p_attrId) << " Value: " << value << "\n";
}
int main()
{
Derived1 derived1;
Derived2 derived2;
printInt(derived1, EAttrs::eAttr1);
printInt(derived2, EAttrs::eAttr1);
}
Check out the DEMO.
P.S. Note the usage of enum class instead of plain enum.
Take a look at this CppCon's talk about Solid principles. Your code might be a good example to apply those principles to.

Polymorphism and shared_ptr passed by reference

I try to send to function a shared_ptr with polymorphic class.
My objective is to find a best way to send my shared_ptr
without increase ref_count.
EDIT: I don't search solution where my shared_ptr is replaced because I want to call shared_ptr.reset() for example.
Currently, void doGenericTemplate(std::shared_ptr<CLASS>& ptr) is what I want in result BUT I prefer a single function in program.
Do you have another solution ?
Moreover, I don't understand why the function void doGeneric(std::shared_ptr<Base>& ptr) doesn't compile (equivalent without shared_ptr work fine: please check doClassic in complete code).
Do you have an explain ?
Thanks you !
Partial code
#include <iostream>
#include <memory>
class Base
{
public:
Base() = default;
virtual ~Base() = default;
virtual void run() = 0;
};
class Derived1: public Base
{
public:
Derived1() = default;
virtual ~Derived1() = default;
void run()
{
std::cout << " Derived1";
}
};
class Derived2: public Base
{
public:
Derived2() = default;
virtual ~Derived2() = default;
void run()
{
std::cout << " Derived2";
}
};
// This function works but increase count
void doGenericCopy(std::shared_ptr<Base> ptr)
{
ptr->run();
std::cout << " Ref count: " << ptr.use_count() << std::endl;
}
// This function works without increase count = OK !
void doSpecificD1(std::shared_ptr<Derived1>& ptr)
{
ptr->run();
std::cout << " Ref count: " << ptr.use_count() << std::endl;
}
// Compilation error = FAILED !
void doGeneric(std::shared_ptr<Base>& ptr)
{
ptr->run();
std::cout << " Ref count: " << ptr.use_count() << std::endl;
}
// Working fine for all Derivate = OK !
template<typename CLASS>
void doGenericTemplate(std::shared_ptr<CLASS>& ptr)
{
ptr->run();
std::cout << " Ref count: " << ptr.use_count() << std::endl;
}
int main()
{
auto d1 = std::make_shared<Derived1>();
auto d2 = std::make_shared<Derived2>();
std::cout << "With copy: " << std::endl;
doGenericCopy(d1);
doGenericCopy(d2);
std::cout << "Specific: " << std::endl;
doSpecificD1(d1);
std::cout << "Template: " << std::endl;
doGenericTemplate(d1);
doGenericTemplate(d2);
// Compilation issue
//doGeneric(d1);
}
Complete code
https://ideone.com/ZL0v7z
Conclusion
Currently in c++, shared_ptr has not in language a specific tools to use polymorphism of class inside template.
The best way is to refactor my code and avoids to manage shared_ptr (ref_count, reset).
Thanks guys !
Do you have another solution ?
Pass object by reference or const reference instead of shared_ptr.
void doGeneric(Base& r)
{
r.run();
}
Firstly - this shows explicitly that you do not store or cache pointer somwhere. Secondly - you avoid ambiguities like the one you presented here.
Do you have an explain ?
Passing shared_ptr<Derived> to function causes implicit cast to shared_ptr<Base>. This new shared_ptr<Base> is temporary, so it can not be cast to shared_ptr<Base> &. This implicit cast would increase ref count even if you could pass it.
A shared_ptr<Base> and shared_ptr<Derived> are unrelated types, except you can implicitly create a shared_ptr<Base> from a shared_ptr<Derived>.
This creation adds a reference count.
If you really, really want to avoid that reference count...
template<class T>
struct shared_ptr_view {
template<class D>
shared_ptr_view( std::shared_ptr<D>& sptr ):
vtable( get_vtable<D>() ),
ptr( std::addressof(sptr) )
{}
shared_ptr_view( shared_ptr_view const& ) = default;
shared_ptr_view() = default;
shared_ptr_view& operator=( shared_ptr_view const& ) = delete;
T* get() const { if(vtable) return vtable->get(ptr); return nullptr; }
void clear() const { if(vtable) vtable->clear(ptr); }
std::shared_ptr<T> copy() const { if(vtable) return vtable->copy(ptr); return {} }
operator std::shared_ptr<T>() const { return copy(); }
T* operator->() const { return get(); }
T& operator*() const { return *get(); }
explicit operator bool() const { return get(); }
std::size_t use_count() const { if (vtable) return vtable->use_count(ptr); return 0; }
private:
struct vtable_t {
T*(*get)(void*) = 0;
std::shared_ptr<T>(*copy)(void*) = 0;
void(*clear)(void*) = 0;
std::size_t(*use_count)(void*) = 0;
};
vtable_t const* vtable = 0;
void* ptr = 0;
template<class D>
static vtable_t create_vtable() {
return {
[](void* ptr)->T*{ return static_cast<std::shared_ptr<D>*>(ptr)->get(); },
[](void* ptr)->std::shared_ptr<T>{ return *static_cast<std::shared_ptr<D>*>(ptr); },
[](void* ptr){ static_cast<std::shared_ptr<D>*>(ptr)->reset(); },
[](void* ptr){ return static_cast<std::shared_ptr<D>*>(ptr)->use_count(); }
};
}
template<class D>
static vtable_t const* get_vtable() {
static const auto vtable = create_vtable<D>();
return &vtable;
}
};
then
void doGeneric( shared_ptr_view<Base> ptr ) {
ptr->run();
std::cout << " Ref count: " << ptr.use_count() << std::endl;
}
does not increase the reference count. I think it is raw insanity.
shared_ptr_view.clear() works, but shared_ptr_view.reset(T*) cannot: a shared_ptr_view<Derived> cannot be reset to point to a Base*.

How to handle Observables with different state-value types in the Observer

(Context and question first, skeleton code at the bottom of the post)
We are creating and implementing a C++ framework to use in environments like Arduino.
For this I want to use the Observer pattern, where any component interested in state-changes of sensors (Observables) can register itself and it will get notified of those changes by the Observable calling the notification() method of the Observer with itself as a parameter.
One Observer can observe multiple Observables, and vice versa.
The problem lies in the fact that the Observer needs to extract the current state of the Observable and do something with it, and this current state can take all forms and sizes, depending on the particular sensor that is the Observable.
It can of course be ordinal values, which are finite and can be coded out, like I did in the code below with the method getValueasInt() but it can also be sensor-specific structures, i.e. for a RealTimeClock, which delivers a struct of date and time values. The struct are of course defined at compile time, and fixed for a specific sensor.
My question: What is the most elegant, and future-modification proof solution or pattern for this ?
Edit: Note that dynamic_cast<> constructions are not possible because of Arduino limitations
I have created the following class-hierarchy (skeleton code):
class SenseNode
{
public:
SenseNode() {};
SenseNode(uint8_t aNodeId): id(aNodeId) {}
virtual ~SenseNode() {}
uint8_t getId() { return id; };
private:
uint8_t id = 0;
};
class SenseStateNode : virtual public SenseNode
{
public:
SenseStateNode(uint8_t aNodeId) : SenseNode(aNodeId) {}
virtual ~SenseStateNode() {}
/** Return current node state interpreted as an integer. */
virtual int getValueAsInt();
};
class SenseObservable: public SenseStateNode
{
public:
SenseObservable(uint8_t aNodeId);
virtual ~SenseObservable();
/** Notify all interested observers of the change in state by calling Observer.notification(this) */
virtual void notifyObservers();
protected:
virtual void registerObserver(SenseObserver *);
virtual void unregisterObserver(SenseObserver *);
};
class SenseObserver: virtual public SenseNode
{
public:
SenseObserver() {};
virtual ~SenseObserver();
/** Called by an Observable that we are observing to inform us of a change in state */
virtual void notification(SenseObservable *observable) {
int v = observable->getValueAsInt(); // works like a charm
DateTime d = observable-> ???? // How should i solve this elegantly?
};
};
My previous answer does not take into account that the same observer might me registered with different observables. I'll try to give a full solution here. The solution is very flexible and scalable but a bit hard to understand as it involves template meta programming (TMP). I'll start by outlining what the end result will look like and then move into the TMP stuff. Brace yourself, this is a LONG answer. Here we go:
We first have, for the sake of the example, three observables, each with its own unique interface which we will want later to access from the observer.
#include <vector>
#include <algorithm>
#include <iostream>
#include <unordered_map>
#include <string>
class observable;
class observer {
public:
virtual void notify(observable& x) = 0;
};
// For simplicity, I will give some default implementation for storing the observers
class observable {
// assumping plain pointers
// leaving it to you to take of memory
std::vector<observer*> m_observers;
public:
observable() = default;
// string id for identifying the concrete observable at runtime
virtual std::string id() = 0;
void notifyObservers() {
for(auto& obs : m_observers) obs->notify(*this);
}
void registerObserver(observer* x) {
m_observers.push_back(x);
}
void unregisterObserver(observer*) {
// give your implementation here
}
virtual ~observable() = default;
};
// our first observable with its own interface
class clock_observable
: public observable {
int m_time;
public:
clock_observable(int time)
: m_time(time){}
// we will use this later
static constexpr auto string_id() {
return "clock_observable";
}
std::string id() override {
return string_id();
}
void change_time() {
m_time++;
notifyObservers(); // notify observes of time change
}
int get_time() const {
return m_time;
}
};
// another observable
class account_observable
: public observable {
double m_balance;
public:
account_observable(double balance)
: m_balance(balance){}
// we will use this later
static constexpr auto string_id() {
return "account_observable";
}
std::string id() override {
return string_id();
}
void deposit_amount(double x) {
m_balance += x;
notifyObservers(); // notify observes of time change
}
int get_balance() const {
return m_balance;
}
};
class temperature_observable
: public observable {
double m_value;
public:
temperature_observable(double value)
: m_value(value){}
// we will use this later
static constexpr auto string_id() {
return "temperature_observable";
}
std::string id() override {
return string_id();
}
void increase_temperature(double x) {
m_value += x;
notifyObservers(); // notify observes of time change
}
int get_temperature() const {
return m_value;
}
};
Notice that each observer exposes an id function returning a string which identifies it. Now, let's assume we want to create an observer which monitors the clock and the account. We could have something like this:
class simple_observer_clock_account
: public observer {
std::unordered_map<std::string, void (simple_observer_clock_account::*) (observable&)> m_map;
void notify_impl(clock_observable& x) {
std::cout << "observer says time is " << x.get_time() << std::endl;
}
void notify_impl(account_observable& x) {
std::cout << "observer says balance is " << x.get_balance() << std::endl;
}
// casts the observable into the concrete type and passes it to the notify_impl
template <class X>
void dispatcher_function(observable& x) {
auto& concrete = static_cast<X&>(x);
notify_impl(concrete);
}
public:
simple_observer_clock_account() {
m_map[clock_observable::string_id()] = &simple_observer_clock_account::dispatcher_function<clock_observable>;
m_map[account_observable::string_id()] = &simple_observer_clock_account::dispatcher_function<account_observable>;
}
void notify(observable& x) override {
auto f = m_map.at(x.id());
(this->*f)(x);
}
};
I am using an unoderded_map so that the correct dispatcher_function will be called depending on the id of the observable. Confirm that this works:
int main() {
auto clock = new clock_observable(100);
auto account = new account_observable(100.0);
auto obs1 = new simple_observer_clock_account();
clock->registerObserver(obs1);
account->registerObserver(obs1);
clock->change_time();
account->deposit_amount(10);
}
A nice thing about this implementation is that if you try to register the observer to a temperature_observable you will get a runtime exception (as the m_map will not contain the relevant temperature_observable id).
This works fine but if you try now to adjust this observer so that it can monitor temperature_observables, things get messy. You either have to go edit the simple_observer_clock_account (which goes against the closed for modification, open for extension principle), or create a new observer as follows:
class simple_observer_clock_account_temperature
: public observer {
std::unordered_map<std::string, void (simple_observer_clock_account_temperature::*) (observable&)> m_map;
// repetition
void notify_impl(clock_observable& x) {
std::cout << "observer1 says time is " << x.get_time() << std::endl;
}
// repetition
void notify_impl(account_observable& x) {
std::cout << "observer1 says balance is " << x.get_balance() << std::endl;
}
// genuine addition
void notify_impl(temperature_observable& x) {
std::cout << "observer1 says temperature is " << x.get_temperature() << std::endl;
}
// repetition
template <class X>
void dispatcher_function(observable& x) {
auto& concrete = static_cast<X&>(x);
notify_impl(concrete);
}
public:
// lots of repetition only to add an extra observable
simple_observer_clock_account_temperature() {
m_map[clock_observable::string_id()] = &simple_observer_clock_account_temperature::dispatcher_function<clock_observable>;
m_map[account_observable::string_id()] = &simple_observer_clock_account_temperature::dispatcher_function<account_observable>;
m_map[temperature_observable::string_id()] = &simple_observer_clock_account_temperature::dispatcher_function<temperature_observable>;
}
void notify(observable& x) override {
auto f = m_map.at(x.id());
(this->*f)(x);
}
};
This works but it is a hell of a lot repetitive for just adding one additional observable. You can also imagine what would happen if you wanted to create any combination (ie account + temperature observable, clock + temp observable, etc). It does not scale at all.
The TMP solution essentially provides a way to do all the above automatically and re-using the overriden implementations as opposed to replicating them again and again. Here is how it works:
We want to build a class hierarchy where the base class will expose a number of virtual notify_impl(T&) method, one for each T concrete observable type that we want to observe. This is achieved as follows:
template <class Observable>
class interface_unit {
public:
virtual void notify_impl(Observable&) = 0;
};
// combined_interface<T1, T2, T3> would result in a class with the following members:
// notify_impl(T1&)
// notify_impl(T2&)
// notify_impl(T3&)
template <class... Observable>
class combined_interface
: public interface_unit<Observable>...{
using self_type = combined_interface<Observable...>;
using dispatcher_type = void (self_type::*)(observable&);
std::unordered_map<std::string, dispatcher_type> m_map;
public:
void map_register(std::string s, dispatcher_type dispatcher) {
m_map[s] = dispatcher;
}
auto get_dispatcher(std::string s) {
return m_map.at(s);
}
template <class X>
void notify_impl(observable& x) {
interface_unit<X>& unit = *this;
// transform the observable to the concrete type and pass to the relevant interface_unit.
unit.notify_impl(static_cast<X&>(x));
}
};
The combined_interface class inherits from each interface_unit and also allows us to register functions to the map, similarly to what we did earlier for the simple_observer_clock_account. Now we need to create a recursive hierarchy where at each step of the recursion we override notify_impl(T&) for each T we are interested in.
// forward declaration
// Iface will be combined_interface<T1, T2>
// The purpose of this class is to implement the virtual methods found in the Iface class, ie notify_impl(T1&), notify_impl(T2&)
// Each ImplUnit provides an override for a single notify_impl(T&)
// Root is the base class of the hierarchy; this will be the data (if any) held by the observer
template <class Root, class Iface, template <class, class> class... ImplUnits>
struct hierarchy;
// recursive
template <class Root, class Iface, template <class, class> class ImplUnit, template <class, class> class... ImplUnits>
struct hierarchy<Root, Iface, ImplUnit, ImplUnits...>
: public ImplUnit< hierarchy<Root, Iface, ImplUnits...>, Root > {
using self_type = hierarchy<Root, Iface, ImplUnit, ImplUnits...>;
using base_type = ImplUnit< hierarchy<Root, Iface, ImplUnits...>, Root >;
public:
template <class... Args>
hierarchy(Args&&... args)
: base_type{std::forward<Args>(args)...} {
using observable_type = typename base_type::observable_type;
Iface::map_register(observable_type::string_id(), &Iface::template notify_impl<observable_type>);
}
};
// specialise if we have iterated through all ImplUnits
template <class Root, class Iface>
struct hierarchy<Root, Iface>
: public Root
, public observer
, public Iface {
public:
template <class... Args>
hierarchy(Args&&... args)
: Root(std::forward<Args>(args)...)
, Iface(){}
};
At each step of the recursion, we register the dispatcher_function to our map.
Finally, we create a class which will be used for our observers:
template <class Root, class Iface, template <class, class> class... ImplUnits>
class observer_base
: public hierarchy<Root, Iface, ImplUnits...> {
public:
using base_type = hierarchy<Root, Iface, ImplUnits...>;
void notify(observable& x) override {
auto f = this->get_dispatcher(x.id());
return (this->*f)(x);
}
template <class... Args>
observer_base(Args&&... args)
: base_type(std::forward<Args>(args)...) {}
};
Let's now create some observables. For simplicity, I assume that the observer has not data:
class observer1_data {};
// this is the ImplUnit for notify_impl(clock_observable&)
// all such implementations must inherit from the Super argument and expose the observable_type type member
template <class Super, class ObserverData>
class clock_impl
: public Super {
public:
using Super::Super;
using observable_type = clock_observable;
void notify_impl(clock_observable& x) override {
std::cout << "observer says time is " << x.get_time() << std::endl;
}
};
template <class Super, class ObserverdData>
class account_impl
: public Super {
public:
using Super::Super;
using observable_type = account_observable;
void notify_impl(account_observable& x) override {
std::cout << "observer says balance is " << x.get_balance() << std::endl;
}
};
template <class Super, class ObserverdData>
class temperature_impl
: public Super {
public:
using Super::Super;
using observable_type = temperature_observable;
void notify_impl(temperature_observable& x) override {
std::cout << "observer says temperature is " << x.get_temperature() << std::endl;
}
};
Now we can easily create any observer we want, no matter what combinations we want to use:
using observer_clock = observer_base<observer1_data,
combined_interface<clock_observable>,
clock_impl>;
using observer_clock_account = observer_base<observer1_data,
combined_interface<clock_observable, account_observable>,
clock_impl, account_impl>;
using observer_clock_account_temperature = observer_base<observer1_data,
combined_interface<clock_observable, account_observable, temperature_observable>,
clock_impl, account_impl, temperature_impl>;
int main() {
auto clock = new clock_observable(100);
auto account = new account_observable(100.0);
auto temp = new temperature_observable(36.6);
auto obs1 = new observer_clock_account_temperature();
clock->registerObserver(obs1);
account->registerObserver(obs1);
temp->registerObserver(obs1);
clock->change_time();
account->deposit_amount(10);
temp->increase_temperature(2);
}
I can appreciate there is a lot to digest. Anyway, I hope it is helpful. If you want to understand in detail the TMP ideas above have a look at the Modern C++ design by Alexandrescu. One of the best I've read.
Let me know if anything is not clear and I will edit the answer.
If the number of sensor types is more or less stable (and it is - the changes are pretty rare in most cases) - then just be prepared on Observer side to get several kind of notifications:
class Observer
{
public:
virtual void notify(SenseNode& node) {
// implement here general actions - like printing: not interested in this
}
virtual void notify(RealTimeClock& node) {
notify(static_cast<SenseNode&>(node));
// by default go to more general function
}
// and follow this pattern - for all nodes you want to handle
// add corresponding notify(T&) function
};
When it happens you have to add new node type - then just add new virtual function to your base Observer class.
To implement this mechanism on Observable side - use double dispatch pattern:
class SenseNode {
public:
virtual void notifyObserver(Observer& observer) {
observer.notify(*this);
}
};
class RealTimeClock : public virtual SenseNode {
public:
virtual void notifyObserver(Observer& observer) {
observer.notify(*this);
// this will select proper Observer::notify(RealTimeClock&)
// because *this is RealTimeCLock
}
};
class SenseObservable: public SenseStateNode
{
public:
virtual void notifyObservers() {
for (auto& observer : observers)
notifyObserver(observer);
}
};
How it works in practice, see live demo
Here is my take. If I understand correctly, each observer knows what concrete observable is monitoring; the problem is that the observer only gets a base class pointer to the concrete observable and hence cannot access the full interface. Assuming you can use static_cast as previous answers have assumed, my idea is to create an additional class which will be responsible for casting the base class pointer to the concrete one, thus giving you access to the concrete interface. The code below uses different names than the ones in your post, but it illustrates the idea:
#include <vector>
#include <algorithm>
#include <iostream>
class observable;
class observer {
public:
virtual void notify(observable&) = 0;
};
// For simplicity, I will give some default implementation for storing the observers
class observable {
// assumping plain pointers
// leaving it to you to take of memory
std::vector<observer*> m_observers;
public:
observable() = default;
void notifyObservers() {
for(auto& obs : m_observers) obs->notify(*this);
}
void registerObserver(observer* x) {
m_observers.push_back(x);
}
void unregisterObserver(observer* x) {
// give your implementation here
}
virtual ~observable() = default;
};
// our first observable with its own interface
class clock_observable
: public observable {
int m_time;
public:
clock_observable(int time)
: m_time(time){}
void change_time() {
m_time++;
notifyObservers(); // notify observes of time change
}
int get_time() const {
return m_time;
}
};
// another observable
class account_observable
: public observable {
double m_balance;
public:
account_observable(double balance)
: m_balance(balance){}
void deposit_amount(double x) {
m_balance += x;
notifyObservers(); // notify observes of time change
}
int get_balance() const {
return m_balance;
}
};
// this wrapper will be inherited and allows you to access the interface of the concrete observable
// all concrete observers should inherit from this class
template <class Observable>
class observer_wrapper
: public observer {
virtual void notify_impl(Observable& x) = 0;
public:
void notify(observable& x) {
notify_impl(static_cast<Observable&>(x));
}
};
// our first clock_observer
class clock_observer1
: public observer_wrapper<clock_observable> {
void notify_impl(clock_observable& x) override {
std::cout << "clock_observer1 says time is " << x.get_time() << std::endl;
}
};
// our second clock_observer
class clock_observer2
: public observer_wrapper<clock_observable> {
void notify_impl(clock_observable& x) override {
std::cout << "clock_observer2 says time is " << x.get_time() << std::endl;
}
};
// our first account_observer
class account_observer1
: public observer_wrapper<account_observable> {
void notify_impl(account_observable& x) override {
std::cout << "account_observer1 says balance is " << x.get_balance() << std::endl;
}
};
// our second account_observer
class account_observer2
: public observer_wrapper<account_observable> {
void notify_impl(account_observable& x) override {
std::cout << "account_observer2 says balance is " << x.get_balance() << std::endl;
}
};
int main() {
auto clock = new clock_observable(100);
auto account = new account_observable(100.0);
observer* clock_obs1 = new clock_observer1();
observer* clock_obs2 = new clock_observer2();
observer* account_obs1 = new account_observer1();
observer* account_obs2 = new account_observer2();
clock->registerObserver(clock_obs1);
clock->registerObserver(clock_obs2);
account->registerObserver(account_obs1);
account->registerObserver(account_obs2);
clock->change_time();
account->deposit_amount(10);
}
As you can see, you do not need to cast every time you create a new observable; the wrapper class does this for you. One issue you may face is registering an observer to the wrong observable; in this case the static_cast would fail but you would get no compilation issues. One way around it is to have the observable expose a string that identifies it and have the observer check that string when it's registering itself. Hope it helps.
You could go with
class SenseStateNode
{
...
virtual ObservableValue& getValue(); //or pointer, comes with different tradeoffs
};
That way, each SenseObservable can return a type derived from ObservableValue. Then, you just have to come up with a usable, generic API for this observable value.
For example, it could be:
class SenseObservable
{
DateTime* asDateTime(); //returns NULL if not a date
float* asFloat(); //returns NULL if not a float
};
The trick is to come with a usable, extensible and generic API for the various observable values. Also, you hve to return them by pointer or reference to not slice them. Then, either the user or the owner has to manage memory.
It may not be the most elegant solution, but the following is an option: define an EventArgs structure that can hold any kind of data, then do a cast in EventHandlers. Here's a snippet I just wrote (not a native speaker of CPP though):
#include <iostream>
#include <map>
#include <vector>
using namespace std;
struct EventArgs;
typedef void (*EventHandler)(EventArgs args);
typedef std::vector<EventHandler> BunchOfHandlers;
typedef std::map<string, BunchOfHandlers> HandlersBySubject;
struct EventArgs
{
void* data;
EventArgs(void* data)
{
this->data = data;
}
};
class AppEvents
{
HandlersBySubject handlersBySubject;
public:
AppEvents()
{
}
void defineSubject(string subject)
{
handlersBySubject[subject] = BunchOfHandlers();
}
void on(string subject, EventHandler handler)
{
handlersBySubject[subject].push_back(handler);
}
void trigger(string subject, EventArgs args)
{
BunchOfHandlers& handlers = handlersBySubject[subject];
for (const EventHandler& handler : handlers)
{
handler(args);
}
}
};
struct FooData
{
int x = 42;
string str = "Test";
};
struct BarData
{
long y = 123;
char c = 'x';
};
void foo_handler_a(EventArgs args)
{
FooData* data = (FooData*)args.data;
cout << "foo_handler_a: " << data->x << " " << data->str << endl;
}
void foo_handler_b(EventArgs args)
{
FooData* data = (FooData*)args.data;
cout << "foo_handler_b: " << data->x << " " << data->str << endl;
}
void bar_handler_a(EventArgs args)
{
BarData* data = (BarData*)args.data;
cout << "bar_handler_a: " << data->y << " " << data->c << endl;
}
void bar_handler_b(EventArgs args)
{
BarData* data = (BarData*)args.data;
cout << "bar_handler_b: " << data->y << " " << data->c << endl;
}
int main()
{
AppEvents* events = new AppEvents();
events->defineSubject("foo");
events->defineSubject("bar");
events->on("foo", foo_handler_a);
events->on("foo", foo_handler_a);
events->on("bar", bar_handler_b);
events->on("bar", bar_handler_b);
events->trigger("foo", EventArgs(new FooData()));
events->trigger("bar", EventArgs(new BarData()));
return 0;
}
Inspired by Backbone events and the general Event Bus pattern.
Difficulty of Observer Pattern in C++ is to handle life-time and un-registration.
You might use the following:
class Observer;
class IObserverNotifier
{
public:
virtual ~IObserverNotifier() = default;
virtual void UnRegister(Observer&) = 0;
};
class Observer
{
public:
explicit Observer() = default;
virtual ~Observer() {
for (auto* abstractObserverNotifier : mAbstractObserverNotifiers)
abstractObserverNotifier->UnRegister(*this);
}
Observer(const Observer&) = delete;
Observer(Observer&&) = delete;
Observer& operator=(const Observer&) = delete;
Observer& operator=(Observer&&) = delete;
void AddObserverNotifier(IObserverNotifier& observerNotifier)
{
mAbstractObserverNotifiers.insert(&observerNotifier);
}
void RemoveObserverNotifier(IObserverNotifier& observerNotifier)
{
mAbstractObserverNotifiers.erase(&observerNotifier);
}
private:
std::set<IObserverNotifier*> mAbstractObserverNotifiers;
};
template<typename ... Params>
class ObserverNotifier : private IObserverNotifier
{
public:
ObserverNotifier() = default;
~ObserverNotifier() {
for (const auto& p : mObserverCallbacks) {
p.first->RemoveObserverNotifier(*this);
}
}
ObserverNotifier(const ObserverNotifier&) = delete;
ObserverNotifier(ObserverNotifier&&) = delete;
ObserverNotifier& operator=(const ObserverNotifier&) = delete;
ObserverNotifier& operator=(ObserverNotifier&&) = delete;
void Register(Observer& observer, std::function<void(Params...)> f) {
mObserverCallbacks.emplace_back(&observer, f);
observer.AddObserverNotifier(*this);
}
void NotifyObservers(Params... args) const
{
for (const auto& p : mObserverCallbacks)
{
const auto& callback = p.second;
callback(args...);
}
}
void UnRegister(Observer& observer) override
{
mObserverCallbacks.erase(std::remove_if(mObserverCallbacks.begin(),
mObserverCallbacks.end(),
[&](const auto& p) { return p.first == &observer;}),
mObserverCallbacks.end());
}
private:
std::vector<std::pair<Observer*, std::function<void(Params...)>>> mObserverCallbacks;
};
And then usage would be something like:
class Sensor
{
public:
void ChangeTime() {
++mTime;
mOnTimeChange.NotifyObservers(mTime);
}
void ChangeTemperature(double delta) {
mTemperature += delta;
mOnTemperatureChange.NotifyObservers(mTemperature);
}
void RegisterTimeChange(Observer& observer, std::function<void(double)> f) { mOnTimeChange.Register(observer, f); }
void RegisterTemperatureChange(Observer& observer, std::function<void(double)> f) { mOnTemperatureChange.Register(observer, f); }
private:
ObserverNotifier<int> mOnTimeChange;
ObserverNotifier<double> mOnTemperatureChange;
int mTime = 0;
double mTemperature = 0;
};
class Ice : public Observer {
public:
void OnTimeChanged(int time) {
mVolume -= mLose;
mOnVolumeChange.NotifyObservers(mVolume);
}
void OnTemperatureChanged(double t) {
if (t <= 0) {
mLose = 0;
} else if (t < 15) {
mLose = 5;
} else {
mLose = 21;
}
}
void RegisterVolumeChange(Observer& observer, std::function<void(double)> f) { mOnVolumeChange.Register(observer, f); }
private:
ObserverNotifier<double> mOnVolumeChange;
double mVolume = 42;
double mLose = 0;
};
class MyObserver : public Observer {
public:
static void OnTimeChange(int t) {
std::cout << "observer says time is " << t << std::endl;
}
static void OnTemperatureChange(double temperature) {
std::cout << "observer says temperature is " << temperature << std::endl;
}
static void OnIceChange(double volume) {
std::cout << "observer says Ice volume is " << volume << std::endl;
}
};
And test it:
int main()
{
Sensor sensor;
Ice ice;
MyObserver observer;
sensor.RegisterTimeChange(observer, &MyObserver::OnTimeChange);
sensor.RegisterTemperatureChange(observer, &MyObserver::OnTemperatureChange);
ice.RegisterVolumeChange(observer, &MyObserver::OnIceChange);
sensor.RegisterTimeChange(ice, [&](int t){ice.OnTimeChanged(t);});
sensor.RegisterTemperatureChange(ice, [&](double t){ice.OnTemperatureChanged(t);});
sensor.ChangeTemperature(0);
sensor.ChangeTime();
sensor.ChangeTemperature(10.3);
sensor.ChangeTime();
sensor.ChangeTime();
sensor.ChangeTemperature(42.1);
sensor.ChangeTime();
}
Demo

"pure virtual function call" error on Debug ONLY

The following "Event" code snippet shows the "pure virtual function call" error. However, as mentioned in the title, it happens only when deploying on DEBUG. What makes me curious is why it works flawlessly on RELEASE and why it does even crash (on DEBUG).
Alternatively, you can see the snippet here.
#include <list>
#include <iostream>
#include <algorithm>
// use base class to resolve the problem of how to put into collection objects of different types
template <typename TPropertyType>
struct PropertyChangedDelegateBase
{
virtual ~PropertyChangedDelegateBase(){};
virtual void operator()(const TPropertyType& t) = 0;
};
template <typename THandlerOwner, typename TPropertyType>
struct PropertyChangedDelegate : public PropertyChangedDelegateBase<TPropertyType>
{
THandlerOwner* pHandlerOwner_;
typedef void (THandlerOwner::*TPropertyChangeHandler)(const TPropertyType&);
TPropertyChangeHandler handler_;
public:
PropertyChangedDelegate(THandlerOwner* pHandlerOwner, TPropertyChangeHandler handler) :
pHandlerOwner_(pHandlerOwner), handler_(handler){}
void operator()(const TPropertyType& t)
{
(pHandlerOwner_->*handler_)(t);
}
};
template<typename TPropertyType>
class PropertyChangedEvent
{
public:
virtual ~PropertyChangedEvent(){};
void add(PropertyChangedDelegateBase<TPropertyType>* const d)
{
std::list<PropertyChangedDelegateBase<TPropertyType>* const>::const_iterator it = std::find(observers_.begin(), observers_.end(), d);
if(it != observers_.end())
throw std::runtime_error("Observer already registered");
observers_.push_back(d);
}
void remove(PropertyChangedDelegateBase<TPropertyType>* const d)
{
std::list<PropertyChangedDelegateBase<TPropertyType>* const>::const_iterator it = std::find(observers_.begin(), observers_.end(), d);
if(it != observers_.end())
observers_.remove(d);
}
// notify
void operator()(const TPropertyType& newValue)
{
std::list<PropertyChangedDelegateBase<TPropertyType>* const>::const_iterator it = observers_.begin();
for(; it != observers_.end(); ++it)
{
(*it)->operator()(newValue);
}
}
protected:
std::list<PropertyChangedDelegateBase<TPropertyType>* const> observers_;
};
class PropertyOwner
{
int property1_;
float property2_;
public:
PropertyChangedEvent<int> property1ChangedEvent;
PropertyChangedEvent<float> property2ChangedEvent;
PropertyOwner() :
property1_(0),
property2_(0.0f)
{}
int property1() const {return property1_;}
void property1(int n)
{
if(property1_ != n)
{
property1_ = n;
property1ChangedEvent(n);
}
}
float property2() const {return property2_;}
void property2(float n)
{
if(property2_ != n)
{
property2_ = n;
property2ChangedEvent(n);
}
}
};
struct PropertyObserver
{
void OnPropertyChanged(const int& newValue)
{
std::cout << "PropertyObserver::OnPropertyChanged() -> new value is: " << newValue << std::endl;
}
};
int _tmain(int argc, _TCHAR* argv[])
{
PropertyOwner propertyOwner;
PropertyObserver propertyObserver;
// register observers
PropertyChangedDelegate<PropertyObserver, int> delegate(&propertyObserver, &PropertyObserver::OnPropertyChanged);
propertyOwner.property1ChangedEvent.add(&delegate); // Ok!
propertyOwner.property1ChangedEvent.add(&PropertyChangedDelegate<PropertyObserver, int>(&propertyObserver, &PropertyObserver::OnPropertyChanged)); // Error: Virtual pure function call (Debug only)
propertyOwner.property1(1);
return getchar();
}
I would assume that the error is misnomer and that the problem is more likely to do with the scope that the second delegate lives. Plus declaring it outside is easier to read.
Passing around an object created on the stack rather than the heap by reference is usually a bad idea. Once the item declaration is out of scope the object is usually forgotten about.
The general issue is that you are binding to a temporary that gets destroyed and thus has an empty vtable and of course it generates a pure virtual call when invoked on the change of the property. If you add a dtor for the base class this is quite easy to observe:
#include <list>
#include <iostream>
#include <algorithm>
// use base class to resolve the problem of how to put into collection objects of different types
template <typename TPropertyType>
struct PropertyChangedDelegateBase
{
virtual ~PropertyChangedDelegateBase(){};
virtual void operator()(const TPropertyType& t) = 0;
};
template <typename THandlerOwner, typename TPropertyType>
struct PropertyChangedDelegate : public PropertyChangedDelegateBase<TPropertyType>
{
THandlerOwner* pHandlerOwner_;
typedef void (THandlerOwner::*TPropertyChangeHandler)(const TPropertyType&);
TPropertyChangeHandler handler_;
public:
PropertyChangedDelegate(THandlerOwner* pHandlerOwner, TPropertyChangeHandler handler) :
pHandlerOwner_(pHandlerOwner), handler_(handler)
{
std::cout << "0x" << std::hex << this << " created!" << std::endl;
}
void operator()(const TPropertyType& t)
{
(pHandlerOwner_->*handler_)(t);
}
~PropertyChangedDelegate()
{
std::cout << "0x" << std::hex << this << " destroyed!" << std::endl;
}
};
template<typename TPropertyType>
class PropertyChangedEvent
{
public:
virtual ~PropertyChangedEvent(){};
void add(PropertyChangedDelegateBase<TPropertyType>* const d)
{
std::list<PropertyChangedDelegateBase<TPropertyType>* const>::const_iterator it = std::find(observers_.begin(), observers_.end(), d);
if (it != observers_.end())
throw std::runtime_error("Observer already registered");
observers_.push_back(d);
}
void remove(PropertyChangedDelegateBase<TPropertyType>* const d)
{
std::list<PropertyChangedDelegateBase<TPropertyType>* const>::const_iterator it = std::find(observers_.begin(), observers_.end(), d);
if (it != observers_.end())
observers_.remove(d);
}
// notify
void operator()(const TPropertyType& newValue)
{
std::list<PropertyChangedDelegateBase<TPropertyType>* const>::const_iterator it = observers_.begin();
for (; it != observers_.end(); ++it)
{
std::cout << "Invoking 0x" << std::hex << *it << std::endl;
(*it)->operator()(newValue);
}
}
protected:
std::list<PropertyChangedDelegateBase<TPropertyType>* const> observers_;
};
class PropertyOwner
{
int property1_;
float property2_;
public:
PropertyChangedEvent<int> property1ChangedEvent;
PropertyChangedEvent<float> property2ChangedEvent;
PropertyOwner() :
property1_(0),
property2_(0.0f)
{}
int property1() const { return property1_; }
void property1(int n)
{
if (property1_ != n)
{
property1_ = n;
property1ChangedEvent(n);
}
}
float property2() const { return property2_; }
void property2(float n)
{
if (property2_ != n)
{
property2_ = n;
property2ChangedEvent(n);
}
}
};
struct PropertyObserver
{
void OnPropertyChanged(const int& newValue)
{
std::cout << "PropertyObserver::OnPropertyChanged() -> new value is: " << newValue << std::endl;
}
};
int main(int argc, char* argv[])
{
PropertyOwner propertyOwner;
PropertyObserver propertyObserver;
// register observers
PropertyChangedDelegate<PropertyObserver, int> delegate(&propertyObserver, &PropertyObserver::OnPropertyChanged);
propertyOwner.property1ChangedEvent.add(&delegate); // Ok!
propertyOwner.property1ChangedEvent.add(&PropertyChangedDelegate<PropertyObserver, int>(&propertyObserver, &PropertyObserver::OnPropertyChanged)); // Error: Virtual pure function call (Debug only)
propertyOwner.property1(1);
return getchar();
}
Basically you are just running into undefined behavior - the object is destroyed in both cases, but in Release the vtable is not destroyed so you get by.
This:
propertyOwner.property1ChangedEvent.add(
&PropertyChangedDelegate<PropertyObserver, int>(
&propertyObserver,
&PropertyObserver::OnPropertyChanged)
);
You are capturing a pointer to a temporary object PropertyChangedDelegate<PropertyObserver, int>. Pointer to this object becomes invalid as soon as function call is over and temporary is destroyed. Dereferencing this pointer is undefined behavior.
In your program, memory ownership relations are critical and you should think them through carefully.
You need to ensure that all your pointers outlive objects that rely on them, either manually:
PropertyChangedDelegate<PropertyObserver, int> delegate2 = {
&propertyObserver,
&PropertyObserver::OnPropertyChanged
};
propertyOwner.property1ChangedEvent.add(&delegate2);
or by using smart pointers (std::unique_ptr<>, std::shared_ptr<>).
Another bug:
C++11 compliant compier should not allow you doing this:
std::list<PropertyChangedDelegateBase<TPropertyType>* const> observers_;
The error I got with Visual Studio 2015 is:
The C++ Standard forbids containers of const elements because allocator is ill-formed.`
See: Does C++11 allow vector<const T>?
Bonus:
Your C++ style looks quite a bit obsolete.
You might want to try automatic type deduction:
for(auto it = observers_.begin(); it != observers_.end(); ++it)
{
(*it)->operator()(newValue);
}
or, better, ranged for loops:
for(auto observer : observers)
{
observer(newValue);
}
You might want to take a look to:
The Definitive C++ Book Guide and List
C++ Core Guidelines

Still an observer pattern?

Usually when I see tutorials about Observer Pattern I see an unique method called notify, but I'm wondering. What if I have different methods that can be called in different moments but needs to notify the others when this happen? Like events, am I doing this wrong? or still begin the observer pattern?
#include <iostream>
#include <algorithm>
#include <vector>
class Observer
{
public:
virtual void notifyBefore() = 0;
virtual void notifyAfter() = 0;
};
class Subject
{
public:
void attachObserver(Observer * observer)
{
observers.push_back(observer);
}
void detachObserver(Observer * observer)
{
auto index = std::find(observers.begin(), observers.end(), observer);
if (index != observers.end())
{
observers.erase(index);
}
}
virtual void notifyBefore()
{
for (auto current : observers)
{
current->notifyBefore();
}
}
virtual void notifyAfter()
{
for (auto current : observers)
{
current->notifyAfter();
}
}
private:
std::vector<Observer *> observers;
};
class ConcreteObserver : public Observer
{
public:
void notifyBefore()
{
std::cout << "You called me before..." << std::endl;
}
void notifyAfter()
{
std::cout << "You called me after..." << std::endl;
}
};
class ConcreteSubject : public Subject
{
public:
};
int main()
{
auto subject = new ConcreteSubject;
subject->attachObserver(new ConcreteObserver);
subject->notifyBefore();
for (int i = 0; i < 5; ++i)
std::cout << i << std::endl;
subject->notifyAfter();
}
Is it still an observer pattern? sure
You created an observer pattern with 2 types of events/notifications.
You could have written it as:
void notify(Type type);
Where Type is the type of event (e.g. an enum).
You can also pass other parameters to indicate other parameters relevant to the event.
void notify(Type type, std::string value);