iMSTK
Interactive Medical Simulation Toolkit
imstkEventObject.h
1 /*
2 ** This file is part of the Interactive Medical Simulation Toolkit (iMSTK)
3 ** iMSTK is distributed under the Apache License, Version 2.0.
4 ** See accompanying NOTICE for details.
5 */
6 
7 #pragma once
8 
9 #include "imstkSpinLock.h"
10 
11 #include <algorithm>
12 #include <deque>
13 #include <functional>
14 #include <list>
15 #include <memory>
16 #include <string>
17 #include <vector>
18 
19 #define SIGNAL(className,signalName) static std::string signalName() { return #className "::"#signalName; }
20 
21 namespace imstk
22 {
23 class EventObject;
24 
32 class Event
33 {
34 public:
35  Event(const std::string type) : m_type(type),m_sender(nullptr) { }
36  virtual~Event() = default;
37 
38 public:
39  std::string m_type;
40  EventObject* m_sender;
41 };
42 
47 class Command
48 {
49 public:
50  Command() : m_call(nullptr),m_event(nullptr) { }
51  Command(std::function<void(Event*)> call,std::shared_ptr<Event> event) : m_call(call),m_event(event) { }
52 
53 public:
58  void invoke()
59  {
60  if (m_event != nullptr)
61  {
62  if (m_call != nullptr)
63  {
64  m_call(m_event.get());
65  }
66  }
67  }
68 
69 public:
70  std::function<void(Event*)> m_call = nullptr;
71  std::shared_ptr<Event> m_event = nullptr;
72 };
73 
74 template<class T,class RecieverType>
75 static void connect(std::shared_ptr<EventObject>,std::string(*)(),
76  std::shared_ptr<RecieverType>,void(RecieverType::*)(T*));
77 template<class T>
78 static void connect(std::shared_ptr<EventObject>, std::string (*)(),
79  std::function<void(T*)>);
80 
81 template<class T, class RecieverType>
82 static void queueConnect(std::shared_ptr<EventObject>, std::string (*)(),
83  std::shared_ptr<RecieverType>, void (RecieverType::*)(T*));
84 template<class T>
85 static void queueConnect(std::shared_ptr<EventObject>, std::string (*)(),
86  std::shared_ptr<EventObject>, std::function<void(T*)>);
87 
88 static void disconnect(std::shared_ptr<EventObject>,
89  std::shared_ptr<EventObject>, std::string (*)());
90 
105 {
106 public:
107  // tuple<IsLambda, Receiver, Receiving Function
108  using Observer = std::tuple<bool, std::weak_ptr<EventObject>, std::function<void (Event*)>>;
109 
110 public:
111  virtual ~EventObject() = default;
112 
113 public:
120  template<typename T>
121  void postEvent(const T& e)
122  {
123  std::shared_ptr<T> ePtr = std::make_shared<T>(e);
124  // Don't overwrite the sender if the user provided one
125  if (ePtr->m_sender == nullptr)
126  {
127  ePtr->m_sender = this;
128  }
129 
130  // For every direct observer
131  // Directly call its function
132  for (auto i = directObservers.begin(); i != directObservers.end(); i++)
133  {
134  if (i->first == e.m_type)
135  {
136  std::vector<Observer>& observers = i->second;
137  for (std::vector<Observer>::iterator j = observers.begin(); j != observers.end();)
138  {
139  bool isLambda = std::get<0>(*j);
140  std::function<void(Event*)> receivingFunc = std::get<2>(*j);
141 
142  // If the receiver or receiving function is nullptr, cleanup
143  // This would occur on deconstruction of a receiver
144  if ((!isLambda && std::get<1>(*j).expired()) || receivingFunc == nullptr)
145  {
146  j = i->second.erase(j);
147  }
148  else
149  {
150  // Call the receiving function
151  receivingFunc(ePtr.get());
152  j++;
153  }
154  }
155  }
156  }
157 
158  // For every queued observer
159  for (auto i = queuedObservers.begin(); i != queuedObservers.end(); i++)
160  {
161  if (i->first == e.m_type)
162  {
163  std::vector<Observer>& observers = i->second;
164  for (std::vector<Observer>::iterator j = observers.begin(); j != observers.end();)
165  {
166  bool isLambda = std::get<0>(*j);
167  std::function<void(Event*)> receivingFunc = std::get<2>(*j);
168 
169  // If the receiver or receiving function is nullptr, cleanup
170  // This would occur on deconstruction of a receiver
171  if ((!isLambda && std::get<1>(*j).expired()) || receivingFunc == nullptr)
172  {
173  j = i->second.erase(j);
174  }
175  else
176  {
177  // Queue the command
178  std::shared_ptr<EventObject> receivingObj = std::get<1>(*j).lock();
179  receivingObj->eventQueueLock.lock();
180  receivingObj->eventQueue.push_back(Command(receivingFunc, ePtr));
181  receivingObj->eventQueueLock.unlock();
182  j++;
183  }
184  }
185  }
186  }
187  }
188 
192  template<typename T>
193  void queueEvent(const T& e)
194  {
195  std::shared_ptr<T> ePtr = std::make_shared<T>(e);
196  // Don't overwrite the sender if the user provided one
197  if (ePtr->m_sender == nullptr)
198  {
199  ePtr->m_sender = this;
200  }
201 
202  eventQueueLock.lock();
203  eventQueue.push_back(Command(nullptr, ePtr));
204  eventQueueLock.unlock();
205  }
206 
210  void doEvent()
211  {
212  // Avoid calling the function within the lock
213  eventQueueLock.lock();
214  if (eventQueue.empty())
215  {
216  eventQueueLock.unlock();
217  return;
218  }
219 
220  Command command = eventQueue.front();
221  eventQueue.pop_front();
222 
223  eventQueueLock.unlock();
224 
225  // Do the calls
226  command.invoke();
227  }
228 
232  void doAllEvents()
233  {
234  // Avoid calling the function within the lock
235  std::list<Command> cmds;
236  eventQueueLock.lock();
237  {
238  while (!eventQueue.empty())
239  {
240  cmds.push_back(eventQueue.front());
241  eventQueue.pop_front();
242  }
243  }
244  eventQueueLock.unlock();
245 
246  // Do the calls
247  for (auto i : cmds)
248  {
249  i.invoke();
250  }
251  }
252 
256  void foreachEvent(std::function<void(Command cmd)> func)
257  {
258  eventQueueLock.lock();
259  for (std::deque<Command>::iterator i = eventQueue.begin(); i != eventQueue.end(); i++)
260  {
261  func(*i);
262  }
263  while (!eventQueue.empty())
264  {
265  Command command = eventQueue.back();
266  eventQueue.pop_back();
267  }
268  eventQueueLock.unlock();
269  }
270 
274  void rforeachEvent(std::function<void(Command cmd)> func)
275  {
276  eventQueueLock.lock();
277  for (std::deque<Command>::reverse_iterator i = eventQueue.rbegin(); i != eventQueue.rend(); i++)
278  {
279  func(*i);
280  }
281  while (!eventQueue.empty())
282  {
283  Command command = eventQueue.back();
284  eventQueue.pop_back();
285  }
286  eventQueueLock.unlock();
287  }
288 
293  void clearEvents()
294  {
295  eventQueueLock.lock();
296  {
297  while (!eventQueue.empty())
298  {
299  Command command = eventQueue.back();
300  eventQueue.pop_back();
301  }
302  }
303  eventQueueLock.unlock();
304  }
305 
306 public:
307  template<class T, class RecieverType>
308  friend void connect(std::shared_ptr<EventObject>, std::string (*)(),
309  std::shared_ptr<RecieverType>, void (RecieverType::*)(T*));
310  template<typename T>
311  friend void connect(std::shared_ptr<EventObject>,
312  std::string (*)(), std::function<void(T*)>);
313 
314  template<typename T, class RecieverType>
315  friend void queueConnect(std::shared_ptr<EventObject>, std::string (*)(),
316  std::shared_ptr<RecieverType>, void (RecieverType::*)(T*));
317  template<class T>
318  friend void queueConnect(std::shared_ptr<EventObject>, std::string (*)(),
319  std::shared_ptr<EventObject>, std::function<void(T*)>);
320 
321  friend void disconnect(std::shared_ptr<EventObject>,
322  std::shared_ptr<EventObject>, std::string (*)());
323 
324 // Use the connect functions
325 private:
326  void addDirectObserver(std::string eventType, Observer observer)
327  {
328  std::vector<std::pair<std::string, std::vector<Observer>>>::iterator i =
329  std::find_if(directObservers.begin(), directObservers.end(),
330  [eventType](const std::pair<std::string, std::vector<Observer>>& j)
331  { return j.first == eventType; });
332  if (i == directObservers.end())
333  {
334  std::pair<std::string, std::vector<Observer>> test = std::pair<std::string, std::vector<Observer>>(eventType, std::vector<Observer>());
335  test.second.push_back(observer);
336  directObservers.push_back(test);
337  }
338  else
339  {
340  i->second.push_back(observer);
341  }
342  }
343 
344  void addQueuedObserver(std::string eventType, Observer observer)
345  {
346  std::vector<std::pair<std::string, std::vector<Observer>>>::iterator i =
347  std::find_if(queuedObservers.begin(), queuedObservers.end(),
348  [eventType](const std::pair<std::string, std::vector<Observer>>& j)
349  { return j.first == eventType; });
350  if (i == queuedObservers.end())
351  {
352  std::pair<std::string, std::vector<Observer>> test = std::pair<std::string, std::vector<Observer>>(eventType, std::vector<Observer>());
353  test.second.push_back(observer);
354  queuedObservers.push_back(test);
355  }
356  else
357  {
358  i->second.push_back(observer);
359  }
360  }
361 
362 protected:
363  ParallelUtils::SpinLock eventQueueLock; // Data lock for the event queue
364  std::deque<Command> eventQueue;
365 
366  // Vectors used as size is generally small
367  std::vector<std::pair<std::string, std::vector<Observer>>> queuedObservers;
368  std::vector<std::pair<std::string, std::vector<Observer>>> directObservers;
369 };
370 
371 #ifdef WIN32
372 #pragma warning(push)
373 #pragma warning(disable: 4505)
374 #endif
375 template<class T, class ReceiverType>
385 static void
386 connect(std::shared_ptr<EventObject> sender, std::string (* senderFunc)(),
387  std::shared_ptr<ReceiverType> receiver, void (ReceiverType::* receiverFunc)(T*))
388 {
389  static_assert(std::is_base_of<EventObject, ReceiverType>::value, "receiver not derived from EventObject");
390 
391  std::function<void(T*)> receiverStdFunc = std::bind(receiverFunc, receiver.get(), std::placeholders::_1);
392  sender->addDirectObserver(senderFunc(), EventObject::Observer(false, receiver, [ = ](Event* e) { receiverStdFunc(static_cast<T*>(e)); }));
393 }
394 
405 template<class T>
406 static void
407 connect(std::shared_ptr<EventObject> sender, std::string (* senderFunc)(),
408  std::function<void(T*)> receiverFunc)
409 {
410  sender->addDirectObserver(senderFunc(), EventObject::Observer(true,
411  std::weak_ptr<EventObject>(), [ = ](Event* e) { receiverFunc(static_cast<T*>(e)); }));
412 }
413 
424 template<class T, class ReceiverType>
425 static void
426 queueConnect(std::shared_ptr<EventObject> sender, std::string (* senderFunc)(),
427  std::shared_ptr<ReceiverType> receiver, void (ReceiverType::* recieverFunc)(T*))
428 {
429  // Ensure sender and reciever are EventObjects
430  static_assert(std::is_base_of<EventObject, ReceiverType>::value, "receiver not derived from EventObject");
431 
432  std::function<void(T*)> recieverStdFunc = std::bind(recieverFunc, receiver.get(), std::placeholders::_1);
433  sender->addQueuedObserver(senderFunc(), EventObject::Observer(false, receiver, [ = ](Event* e) { recieverStdFunc(static_cast<T*>(e)); }));
434 }
435 
449 template<class T>
450 static void
451 queueConnect(std::shared_ptr<EventObject> sender, std::string (* senderFunc)(),
452  std::shared_ptr<EventObject> receiver, std::function<void(T*)> recieverFunc)
453 {
454  sender->addQueuedObserver(senderFunc(), EventObject::Observer(true, receiver, [ = ](Event* e) { recieverFunc(static_cast<T*>(e)); }));
455 }
456 
468 static void
469 disconnect(std::shared_ptr<EventObject> sender,
470  std::shared_ptr<EventObject> reciever, std::string (* senderFunc)())
471 {
472  const std::string eventType = senderFunc();
473 
474  auto i1 = std::find_if(sender->directObservers.begin(), sender->directObservers.end(),
475  [eventType](const std::pair<std::string, std::vector<EventObject::Observer>>& j) { return j.first == eventType; });
476  if (i1 != sender->directObservers.end())
477  {
478  auto j = std::find_if(i1->second.begin(), i1->second.end(), [reciever](const EventObject::Observer& k) { return std::get<1>(k).lock() == reciever; });
479  i1->second.erase(j);
480  }
481 
482  auto i2 = std::find_if(sender->queuedObservers.begin(), sender->queuedObservers.end(),
483  [eventType](const std::pair<std::string, std::vector<EventObject::Observer>>& j) { return j.first == eventType; });
484  if (i2 != sender->queuedObservers.end())
485  {
486  auto j = std::find_if(i2->second.begin(), i2->second.end(), [reciever](const EventObject::Observer& k) { return std::get<1>(k).lock() == reciever; });
487  i2->second.erase(j);
488  }
489 }
490 
491 #ifdef WIN32
492 #pragma warning(pop)
493 #endif
494 } // namespace imstk
The SpinLock class.
Definition: imstkSpinLock.h:20
void rforeachEvent(std::function< void(Command cmd)> func)
thread safe reverse loop over all event commands, one can implement a custom handler ...
void doAllEvents()
Do all the events in the event queue.
void doEvent()
Do an event, if none exists return.
void foreachEvent(std::function< void(Command cmd)> func)
Thread safe loop over all event commands, one can implement a custom handler.
Base class for events which contain a type, priority, and data priority defaults to 0 and uses a grea...
void clearEvents()
Removes all events from queue cleans up copies of the event.
Compound Geometry.
void invoke()
Call the underlying function if present then delete the event data.
EventObject is the base class for all objects in iMSTK that can receive and emit events. It supports direct and queued observer functions. Direct observers receive events immediately on the same thread This can either be posted on an object or be a function pointer Queued observers receive events within their queue which they can process whenever they like. These can be connected with the connect/queuedConnect/disconnect functions Lambda recievers cannot be disconnected unless all receivers to a signal are removed.
void queueEvent(const T &e)
Queues event directly to this.
void postEvent(const T &e)
Emits the event Direct observers will be immediately called, in sync Queued observers will receive th...
Stores everything needed to invoke an event A call may not be present, in which case invoke doesn&#39;t d...