ObserverPattern
observer.h
1 #include <vector>
2 #include <type_traits>
3 #include <cassert>
4 #include <algorithm>
5 #include <memory>
6 
7 namespace Observer
8 {
9 
11 
16 template <typename... T_Listeners>
17 class Listener : public T_Listeners... {
18 public:
19  using ListenerType = Listener<T_Listeners...>;
20 
21  virtual ~Listener() {}
22 };
23 
25 
30 template <class T_Listener>
32 {
33 public:
34  virtual ~RawContainer() {}
35 
37 
40  void attach(T_Listener* listener)
41  {
42  assert(listener);
43  m_listeners.push_back(listener);
44  }
45 
47 
50  void detach(T_Listener* listener)
51  {
52  m_listeners.erase(std::remove_if(m_listeners.begin(), m_listeners.end(), listener));
53  }
54 
55 protected:
56 
58 
62  template <typename... Fn_Args>
63  void notify(void (T_Listener::*fn)(Fn_Args...), Fn_Args&&... args)
64  {
65  for (auto& l : m_listeners)
66  (l->*fn)(std::forward<Fn_Args>(args)...);
67  }
68 
69 private:
70  std::vector<T_Listener*> m_listeners;
71 };
72 
74 
79 template <class T_Listener>
81 {
82 public:
83  virtual ~SmartContainer() {}
84 
86 
90  void attach(const std::shared_ptr<T_Listener>& listener)
91  {
92  assert(listener);
93  m_listeners.push_back(listener);
94  }
95 
97 
100  void detach(const std::shared_ptr<T_Listener>& listener)
101  {
102  // Erase the given listener and all listeners, which have already expired.
103  m_listeners.erase(std::remove_if(m_listeners.begin(), m_listeners.end(),
104  [&listener](const auto& weakListener)
105  {
106  auto ptr = weakListener.lock();
107  return ptr == listener || ptr.empty();
108  }));
109  }
110 
111 protected:
112 
114 
118  template <typename... Fn_Args>
119  void notify(void (T_Listener::*fn)(Fn_Args...), Fn_Args&&... args)
120  {
121  for (auto& weakListener : m_listeners)
122  if (auto listener = weakListener.lock())
123  (listener.get()->*fn)(std::forward<Fn_Args>(args)...);
124  }
125 
126 private:
127  std::vector<std::weak_ptr<T_Listener>> m_listeners;
128 };
129 
130 
131 namespace detail
132 {
134 
137 template <typename T, typename... Args>
138 struct contains
139  : public std::disjunction<std::is_same<T, Args>...>
140 { };
141 
143 
146 template <bool Contains, class T_Container>
147 struct cond {
148  template<class T_Source, class T_Listener>
149  static void attach(T_Source* s, const T_Listener& l) {
150  static_cast<T_Container*>(s)->attach(l);
151  }
152 
153  template<class T_Source, class T_Listener>
154  static void detach(T_Source* s, const T_Listener& l) {
155  static_cast<T_Container*>(s)->detach(l);
156  }
157 };
158 
160 
163 template<class T_Container>
164 struct cond<false, T_Container> {
165  template<class T_Source, class T_Listener>
166  static void attach(T_Source*, T_Listener) {}
167 
168  template<class T_Source, class T_Listener>
169  static void detach(T_Source*, T_Listener) {}
170 };
171 }
172 
173 
175 
187 template <template<class> class T_Container, class... T_Listeners>
188 class Source
189  : public T_Container<T_Listeners>...
190 {
191  using SourceType = Source<T_Container, T_Listeners...>;
192 public:
193 
195 
200  template<typename... Args>
201  void attach(Listener<Args...>* listener)
202  {
203  using namespace detail;
204  (cond<contains<Args, T_Listeners...>::value, T_Container<Args>>::attach(this, listener), ...);
205  }
206 
208 
213  template<typename... Args>
214  void attach(const std::shared_ptr<Listener<Args...>>& listener)
215  {
216  using namespace detail;
217  (cond<contains<Args, T_Listeners...>::value, T_Container<Args>>::attach(this, listener), ...);
218  }
219 
221  template<class T>
222  void attach(const std::shared_ptr<T>& listener)
223  {
224  attach(std::static_pointer_cast<typename T::ListenerType>(listener));
225  }
226 
228  template<typename... Args>
229  void detach(Listener<Args...>* listener)
230  {
231  using namespace detail;
232  (cond<contains<Args, T_Listeners...>::value, T_Container<Args>>::detach(this, listener), ...);
233  }
234 
236  template<typename... Args>
237  void detach(const std::shared_ptr<Listener<Args...>>& listener)
238  {
239  using namespace detail;
240  (cond<contains<Args, T_Listeners...>::value, T_Container<Args>>::detach(this, listener), ...);
241  }
242 
244  template<class T>
245  void detach(const std::shared_ptr<T>& listener)
246  {
247  detach(std::static_pointer_cast<typename T::ListenerType>(listener));
248  }
249 
250 protected:
251 
253 
256  template <typename T, typename... Fn_Args>
257  void notify(
258  void (T::*fn)(Fn_Args...),
259  Fn_Args&&... args)
260  {
261  T_Container<T>::notify(fn, std::forward<Fn_Args>(args)...);
262  }
263 };
264 
266 template <class... T_Listeners>
267 using RawSource = Source<RawContainer, T_Listeners...>;
268 
270 template <class... T_Listeners>
271 using SmartSource = Source<SmartContainer, T_Listeners...>;
272 
273 } // namespace Observer
void detach(T_Listener *listener)
Detach listener listener from this container.
Definition: observer.h:50
void notify(void(T_Listener::*fn)(Fn_Args...), Fn_Args &&... args)
Call a notification function as specified by the first parameter.
Definition: observer.h:119
A container of listeners, which holds weak pointers.
Definition: observer.h:80
Base class for all sources.
Definition: observer.h:188
A container of listeners, which holds raw pointers.
Definition: observer.h:31
void detach(const std::shared_ptr< T_Listener > &listener)
Detach listener listener from this container.
Definition: observer.h:100
void attach(const std::shared_ptr< Listener< Args... >> &listener)
Attach a listener object, which implements listeners given by Args.
Definition: observer.h:214
void detach(Listener< Args... > *listener)
Detach a listener object, which implements listeners given by Args.
Definition: observer.h:229
void attach(const std::shared_ptr< T > &listener)
Convenience method to overcome the covariant issue with smart pointers.
Definition: observer.h:222
void attach(T_Listener *listener)
Attach listener listener to this container.
Definition: observer.h:40
void notify(void(T_Listener::*fn)(Fn_Args...), Fn_Args &&... args)
Call a notification function as specified by the first parameter.
Definition: observer.h:63
Base class for all listeners.
Definition: observer.h:17
void attach(Listener< Args... > *listener)
Attach a listener object, which implements listeners given by Args.
Definition: observer.h:201
void notify(void(T::*fn)(Fn_Args...), Fn_Args &&... args)
Call a notification function as specified by the first parameter.
Definition: observer.h:257
void detach(const std::shared_ptr< T > &listener)
Convenience method to overcome the covariant issue with smart pointers.
Definition: observer.h:245
Convenience struct for identifying if a class T is in pack Args.
Definition: observer.h:138
void detach(const std::shared_ptr< Listener< Args... >> &listener)
Detach a listener object, which implements listeners given by Args.
Definition: observer.h:237
void attach(const std::shared_ptr< T_Listener > &listener)
Attach listener listener to this container.
Definition: observer.h:90
Conditionally calls attach and detach methods of the source object.
Definition: observer.h:147