openMSX
Subject.hh
Go to the documentation of this file.
1 #ifndef SUBJECT_HH
2 #define SUBJECT_HH
3 
4 #include "Observer.hh"
5 #include "ranges.hh"
6 #include "stl.hh"
7 #include <algorithm>
8 #include <vector>
9 #include <cassert>
10 
11 namespace openmsx {
12 
17 template<typename T> class Subject
18 {
19 public:
20  void attach(Observer<T>& observer);
21  void detach(Observer<T>& observer);
22  [[nodiscard]] bool anyObservers() const { return !observers.empty(); }
23 
24 protected:
25  Subject() = default;
27  void notify() const;
28 
29 private:
30  enum NotifyState {
31  IDLE, // no notify in progress
32  IN_PROGRESS, // notify in progress, no detach
33  DETACH, // notify in progress, some observer(s) have been detached
34  };
35 
36  mutable std::vector<Observer<T>*> observers; // unordered
37  mutable NotifyState notifyState = IDLE;
38 };
39 
40 template<typename T> Subject<T>::~Subject()
41 {
42  assert(notifyState == IDLE);
43  auto copy = observers;
44  for (auto& o : copy) {
45  o->subjectDeleted(*static_cast<const T*>(this));
46  }
47  assert(observers.empty());
48 }
49 
50 template<typename T> void Subject<T>::attach(Observer<T>& observer)
51 {
52  assert(notifyState == IDLE);
53  observers.push_back(&observer);
54 }
55 
56 template<typename T> void Subject<T>::detach(Observer<T>& observer)
57 {
58  auto it = rfind_unguarded(observers, &observer);
59  if (notifyState == IDLE) {
60  move_pop_back(observers, it);
61  } else {
62  *it = nullptr; // mark for removal
63  notifyState = DETACH; // schedule actual removal pass
64  }
65 }
66 
67 template<typename T> void Subject<T>::notify() const
68 {
69  assert(notifyState == IDLE);
70  notifyState = IN_PROGRESS;
71 
72  for (auto& o : observers) {
73  try {
74  o->update(*static_cast<const T*>(this));
75  } catch (...) {
76  assert(false && "Observer::update() shouldn't throw");
77  }
78  }
79 
80  if (notifyState == DETACH) {
81  observers.erase(ranges::remove(observers, nullptr), observers.end());
82  }
83  notifyState = IDLE;
84 }
85 
86 } // namespace openmsx
87 
88 #endif
Generic Gang-of-Four Observer class, templatized edition.
Definition: Observer.hh:10
Generic Gang-of-Four Subject class of the Observer pattern, templatized edition.
Definition: Subject.hh:18
void detach(Observer< T > &observer)
Definition: Subject.hh:56
void notify() const
Definition: Subject.hh:67
Subject()=default
void attach(Observer< T > &observer)
Definition: Subject.hh:50
bool anyObservers() const
Definition: Subject.hh:22
This file implemented 3 utility functions:
Definition: Autofire.cc:9
constexpr auto IDLE
Definition: WD2793.cc:40
auto remove(ForwardRange &&range, const T &value)
Definition: ranges.hh:202
auto copy(InputRange &&range, OutputIter out)
Definition: ranges.hh:178
void move_pop_back(VECTOR &v, typename VECTOR::iterator it)
Erase the pointed to element from the given vector.
Definition: stl.hh:133
auto rfind_unguarded(RANGE &range, const VAL &val, Proj proj={})
Similar to the find(_if)_unguarded functions above, but searches from the back to front.
Definition: stl.hh:108