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  bool anyObservers() const { return !observers.empty(); }
23 
24 protected:
25  Subject() = default;
26  ~Subject();
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  o->update(*static_cast<const T*>(this));
74  }
75 
76  if (notifyState == DETACH) {
77  observers.erase(ranges::remove(observers, nullptr), observers.end());
78  }
79  notifyState = IDLE;
80 }
81 
82 } // namespace openmsx
83 
84 #endif
auto copy(InputRange &&range, OutputIter out)
Definition: ranges.hh:149
auto remove(ForwardRange &&range, const T &value)
Definition: ranges.hh:167
void notify() const
Definition: Subject.hh:67
void move_pop_back(VECTOR &v, typename VECTOR::iterator it)
Erase the pointed to element from the given vector.
Definition: stl.hh:191
void attach(Observer< T > &observer)
Definition: Subject.hh:50
Thanks to enen for testing this on a real cartridge:
Definition: Autofire.cc:5
bool anyObservers() const
Definition: Subject.hh:22
Generic Gang-of-Four Subject class of the Observer pattern, templatized edition.
Definition: Subject.hh:17
void detach(Observer< T > &observer)
Definition: Subject.hh:56
Generic Gang-of-Four Observer class, templatized edition.
Definition: Observer.hh:9
auto rfind_unguarded(RANGE &range, const VAL &val)
Similar to the find(_if)_unguarded functions above, but searches from the back to front...
Definition: stl.hh:166
Subject()=default