SMTK  @SMTK_VERSION@
Simulation Modeling Tool Kit
Observers.h
1 //=========================================================================
2 // Copyright (c) Kitware, Inc.
3 // All rights reserved.
4 // See LICENSE.txt for details.
5 //
6 // This software is distributed WITHOUT ANY WARRANTY; without even
7 // the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR
8 // PURPOSE. See the above copyright notice for more information.
9 //=========================================================================
10 #ifndef smtk_common_Observers_h
11 #define smtk_common_Observers_h
12 
13 #include <functional>
14 #include <iostream>
15 #include <limits>
16 #include <map>
17 #include <mutex>
18 #include <set>
19 #include <string>
20 #include <type_traits>
21 #include <utility>
22 
23 #define ADD_OBSERVER(key, observers, observer, priority, initialize) \
24  key = (observers)->insert( \
25  observer, \
26  priority, \
27  initialize, \
28  std::string(__FILE__) + std::string(": ") + std::to_string(__LINE__))
29 
30 namespace smtk
31 {
32 namespace common
33 {
34 
65 template<typename Observer, bool DebugObservers = false>
66 class Observers
67 {
68  friend class Key;
69 
70 public:
73  typedef int Priority;
74 
75 private:
77  struct InternalKey : std::pair<int, int>
78  {
79  // By default, construct a null key.
80  InternalKey()
81  : std::pair<Priority, int>(std::numeric_limits<Priority>::lowest(), -1)
82  {
83  }
84  InternalKey(int i, int j)
85  : std::pair<int, int>(i, j)
86  {
87  }
88  ~InternalKey() = default;
89 
90  bool assigned() const { return this->second != -1; }
91 
92  bool operator<(const InternalKey& rhs) const
93  {
94  return this->first == rhs.first ? this->second < rhs.second : rhs.first < this->first;
95  }
96  };
97 
98 public:
99  class Key final : InternalKey
100  {
101  friend class Observers;
102 
103  public:
104  Key()
105  : InternalKey()
106  , m_observers(nullptr)
107  {
108  }
109 
110  Key(const Key&) = delete;
111  Key& operator=(const Key&) = delete;
112 
113  Key(Key&& key) noexcept
114  : InternalKey(std::move(key))
115  {
116  m_observers = key.m_observers;
117  if (m_observers)
118  {
119  std::unique_lock<std::mutex> lock(m_observers->m_mutex);
120  m_observers->m_keys[*this] = this;
121  key.m_observers = nullptr;
122  }
123  }
124 
125  Key& operator=(Key&& key) noexcept
126  {
127  if (m_observers)
128  {
129  m_observers->erase(*this);
130  }
131 
132  static_cast<InternalKey&>(*this) = std::move(static_cast<InternalKey&>(key));
133  m_observers = key.m_observers;
134 
135  if (m_observers)
136  {
137  std::unique_lock<std::mutex> lock(m_observers->m_mutex);
138  m_observers->m_keys[*this] = this;
139  key.m_observers = nullptr;
140  }
141  return *this;
142  }
143 
144  ~Key()
145  {
146  if (m_observers)
147  {
148  m_observers->erase(*this);
149  m_observers = nullptr;
150  }
151  }
152 
153  bool operator==(const Key& rhs)
154  {
155  return this->first == rhs.first && this->second == rhs.second;
156  }
157  bool operator!=(const Key& rhs) { return !((*this) == rhs); }
158 
159  bool assigned() const { return this->InternalKey::assigned(); }
160 
161  void release()
162  {
163  if (m_observers)
164  {
165  std::unique_lock<std::mutex> lock(m_observers->m_mutex);
166  m_observers->m_keys.erase(*this);
167  }
168  m_observers = nullptr;
169  }
170 
171  private:
172  Key(const InternalKey& key, Observers* observers)
173  : InternalKey(key)
174  , m_observers(observers)
175  {
176  if (m_observers)
177  {
178  m_observers->m_keys[key] = this;
179  }
180  }
181 
182  Observers* m_observers;
183  };
184 
187  typedef std::function<void(Observer&)> Initializer;
188 
189  Observers()
190  : m_initializer()
191  {
192  }
193  Observers(Initializer&& initializer)
194  : m_initializer(initializer)
195  {
196  }
197 
198  ~Observers()
199  {
200  std::unique_lock<std::mutex> lock(m_mutex);
201  for (auto& keypair : m_keys)
202  {
203  keypair.second->m_observers = nullptr;
204  }
205  }
206 
209  template<class... Types>
210  auto operator()(Types&&... args) -> decltype(std::declval<Observer>()(args...))
211  {
212  return m_override ? m_override.operator()(std::forward<Types>(args)...)
213  : callObserversDirectly(std::forward<Types>(args)...);
214  }
215 
218  template<class... Types>
219  auto callObserversDirectly(Types&&... args) -> typename std::enable_if<
220  std::is_integral<decltype(std::declval<Observer>()(args...))>::value,
221  decltype(std::declval<Observer>()(args...))>::type
222  {
223  decltype(std::declval<Observer>()(args...)) result = 0;
224 
225  // Toggle m_observing flag to enable caching of requests to erase observers.
226  m_observing = true;
227  for (auto& entry : m_observers)
228  {
229  // During iteration, some observers may have requested the erasure of
230  // other observers. Rather than directly remove them from the map and
231  // invalidate the iteration loop, we cache these requests and prevent
232  // these observers from being called (as though they were erased).
233  if (m_toErase.find(entry.first) == m_toErase.end())
234  {
235  if (DebugObservers)
236  {
237  std::cerr << "Calling observer (" << entry.first.first << ", " << entry.first.second
238  << "): " << m_descriptions[entry.first] << std::endl;
239  }
240  if (entry.first.assigned())
241  {
242  result |= entry.second(std::forward<Types>(args)...);
243  }
244  }
245  else if (DebugObservers)
246  {
247  std::cerr << "Skipping erased observer (" << entry.first.first << ", " << entry.first.second
248  << "): " << m_descriptions[entry.first] << std::endl;
249  }
250  }
251  // Now that the iteration loop is complete, it is now safe to erase
252  // observers again.
253  m_observing = false;
254 
255  // Remove observers that were marked for erasure during observation.
256  for (auto& toErase : m_toErase)
257  {
258  if (DebugObservers)
259  {
260  std::cerr << "Erasing observer (" << toErase.first << ", " << toErase.second
261  << "): " << m_descriptions[toErase] << std::endl;
262  }
263  erase(toErase);
264  }
265  m_toErase.clear();
266 
267  return result;
268  }
269 
272  template<class... Types>
273  auto callObserversDirectly(Types&&... args) -> typename std::enable_if<
274  !std::is_integral<decltype(std::declval<Observer>()(args...))>::value,
275  decltype(std::declval<Observer>()(args...))>::type
276  {
277  // Toggle m_observing flag to enable caching of requests to erase observers.
278  m_observing = true;
279  for (auto& entry : m_observers)
280  {
281  // During iteration, some observers may have requested the erasure of
282  // other observers. Rather than directly remove them from the map and
283  // invalidate the iteration loop, we cache these requests and prevent
284  // these observers from being called (as though they were erased).
285  if (m_toErase.find(entry.first) == m_toErase.end())
286  {
287  if (DebugObservers)
288  {
289  std::cerr << "Calling observer (" << entry.first.first << ", " << entry.first.second
290  << "): " << m_descriptions[entry.first] << std::endl;
291  }
292  if (entry.first.assigned())
293  {
294  entry.second(std::forward<Types>(args)...);
295  }
296  }
297  else if (DebugObservers)
298  {
299  std::cerr << "Skipping erased observer (" << entry.first.first << ", " << entry.first.second
300  << "): " << m_descriptions[entry.first] << std::endl;
301  }
302  }
303  // Now that the iteration loop is complete, it is now safe to erase
304  // observers again.
305  m_observing = false;
306 
307  // Remove observers that were marked for erasure during observation.
308  for (auto& toErase : m_toErase)
309  {
310  if (DebugObservers)
311  {
312  std::cerr << "Erasing observer (" << toErase.first << ", " << toErase.second
313  << "): " << m_descriptions[toErase] << std::endl;
314  }
315  erase(toErase);
316  }
317  m_toErase.clear();
318  }
319 
329  Key insert(Observer fn, Priority priority, bool initialize, std::string description = "")
330  {
331  // An observer's handle id (the second value in its key) defines the order
332  // in which the observer is called at a specific priority level. We
333  // monotonically increase this value for each priority value we encounter.
334  int handleId;
335  if (m_observers.empty())
336  {
337  // If there are no observers, then this observer is the first of its
338  // priority level.
339  handleId = 0;
340  }
341  else
342  {
343  // Search for the the last observer (the one with the highest handle id)
344  // at the requested priority. To do this, we first access the first
345  // observer at the priority lower than the requested priority. We then
346  // access the observer before it.
347  typename std::map<InternalKey, Observer>::iterator upper;
348  if (priority != std::numeric_limits<Priority>::lowest())
349  {
350  auto key = InternalKey(priority - 1, -1);
351  upper = m_observers.upper_bound(key);
352 
353  // If the found observer is the first observer in the map, the incident
354  // observer is the first at its priority level. It will be assigned as
355  // such by the subsequent logic of this method without the iterator
356  // decrement.
357  if (upper != m_observers.begin())
358  {
359  --upper;
360  }
361  }
362  else
363  {
364  // If the requested priority is unset, we return the
365  // last observer in the map.
366  upper = --m_observers.end();
367  }
368  // If the observer we found is at the same priority as the incident
369  // observer, the new handle id is one higher than the found observer's
370  // handle id. Otherwise, this is the first observer at this priority
371  // level.
372  handleId = (upper->first.first == priority ? upper->first.second + 1 : 0);
373  }
374  InternalKey handle = InternalKey(priority, handleId);
375  if (initialize && m_initializer)
376  {
377  m_initializer(fn);
378  }
379 
380  m_descriptions.insert(std::make_pair(handle, description));
381  if (DebugObservers)
382  {
383  std::cerr << "Inserting observer (" << handle.first << ", " << handle.second
384  << "): " << description << std::endl;
385  }
386  return m_observers.insert(std::make_pair(handle, fn)).second ? Key(handle, this) : Key();
387  }
388 
389  Key insert(Observer fn, std::string description = "")
390  {
391  return insert(fn, std::numeric_limits<Priority>::lowest(), true, description);
392  }
393 
396  std::size_t erase(Key& handle)
397  {
398  handle.release();
399 
400  if (m_observing)
401  {
402  if (DebugObservers)
403  {
404  std::cerr << "Queueing observer erasure (" << handle.first << ", " << handle.second
405  << "): " << m_descriptions[handle] << std::endl;
406  }
407  m_toErase.insert(handle);
408  return m_observers.size() - m_toErase.size();
409  }
410  if (DebugObservers)
411  {
412  std::cerr << "Immediate observer erasure (" << handle.first << ", " << handle.second
413  << "): " << m_descriptions[handle] << std::endl;
414  }
415  return erase(static_cast<InternalKey&>(handle));
416  }
417 
419  Observer find(const Key& handle) const
420  {
421  auto entry = m_observers.find(handle);
422  return entry == m_observers.end() ? nullptr : entry->second;
423  }
424 
426  std::size_t size() const { return m_observers.size(); }
427 
430  void overrideWith(Observer fn) { m_override = fn; }
431 
434  void removeOverride() { m_override = Observer(); }
435 
436  const Initializer& initializer() const { return m_initializer; }
437 
438  void setInitializer(Initializer fn) { m_initializer = fn; }
439 
440  std::string description(Key handle) const { return m_descriptions[handle]; }
441 
442 protected:
443  // A map of observers. The observers are held in a map so that they can be
444  // referenced (and therefore removed) at a later time using the observer's
445  // associated key.
446  std::map<InternalKey, Observer> m_observers;
447 
448  // A map of descriptions. A descriptions can be manually added to an observer
449  // during its insertion, or it can automatically refer to the location of the
450  // observer's insertion in the source code if the ADD_OBSERVER macro is used.
451  std::map<InternalKey, std::string> m_descriptions;
452 
453  // A functor to override the default behavior of the Observers' call method.
454  Observer m_override;
455 
456  // A functor to override the default initialize method.
457  Initializer m_initializer;
458 
459 private:
460  std::size_t erase(const InternalKey& key)
461  {
462  std::unique_lock<std::mutex> lock(m_mutex);
463  m_keys.erase(key);
464  return m_observers.erase(key);
465  }
466 
467  bool m_observing{ false };
468  std::set<InternalKey> m_toErase;
469 
470  std::mutex m_mutex;
471  std::map<InternalKey, Key*> m_keys;
472 };
473 } // namespace common
474 } // namespace smtk
475 
476 #endif // smtk_common_Observers_h
smtk
The main namespace for the Simulation Modeling Tool Kit (SMTK).
Definition: doc.h:33
smtk::common::Observers::find
Observer find(const Key &handle) const
Return the observer for the given key if one exists or nullptr otherwise.
Definition: Observers.h:419
smtk::common::Observers::Key
Definition: Observers.h:99
smtk::common::Observers::operator()
auto operator()(Types &&... args) -> decltype(std::declval< Observer >()(args...))
The call operator calls each of its Observer functors in sequence if there is no override functor def...
Definition: Observers.h:210
smtk::common::Observers::overrideWith
void overrideWith(Observer fn)
Replace the default implementation (calling each Observer functor in sequence) with a new behavior.
Definition: Observers.h:430
smtk::common::Observers
An Observer is a functor that is called when certain actions are performed.
Definition: Observers.h:66
smtk::common::Observers::Priority
int Priority
A value to indicate the relative order in which an observer should be called.
Definition: Observers.h:73
smtk::common::Observers::erase
std::size_t erase(Key &handle)
Indicate that an observer should no longer be called.
Definition: Observers.h:396
smtk::common::Observers::removeOverride
void removeOverride()
Remove the overriding behavior, restoring the default behavior (calling each Observer functor when Ob...
Definition: Observers.h:434
smtk::common::Observers::Key::Observers
friend class Observers
Operation observers are a specialization of the common SMTK observer pattern.
Definition: Observers.h:101
smtk::common::Observers::Initializer
std::function< void(Observer &)> Initializer
A functor to optionally initialize Observers as they are inserted into the Observers instance.
Definition: Observers.h:187
smtk::common::Observers::size
std::size_t size() const
Return the number of Observer functors in this instance.
Definition: Observers.h:426
smtk::common::Observers::callObserversDirectly
auto callObserversDirectly(Types &&... args) -> typename std::enable_if< !std::is_integral< decltype(std::declval< Observer >()(args...))>::value, decltype(std::declval< Observer >()(args...))>::type
For Observer functors that do not return an integral value, simply call all Observer functors.
Definition: Observers.h:273
smtk::operation::Observer
std::function< int(const Operation &, EventType, Operation::Result)> Observer
Signature for methods observing operations.
Definition: Observer.h:42
smtk::common::Observers::callObserversDirectly
auto callObserversDirectly(Types &&... args) -> typename std::enable_if< std::is_integral< decltype(std::declval< Observer >()(args...))>::value, decltype(std::declval< Observer >()(args...))>::type
For Observer functors that return an integral value, call all Observer functors and aggregate their o...
Definition: Observers.h:219
smtk::common::Observers::insert
Key insert(Observer fn, Priority priority, bool initialize, std::string description="")
Ask to receive notification (and possibly a chance to respond to) events.
Definition: Observers.h:329