PageRenderTime 25ms CodeModel.GetById 24ms RepoModel.GetById 0ms app.codeStats 0ms

/libs/poco/include/Poco/AbstractEvent.h

https://github.com/jehc/openFrameworks
C Header | 340 lines | 126 code | 37 blank | 177 comment | 3 complexity | 37587c4cd4788825ecc5933a7a03866e MD5 | raw file
  1. //
  2. // AbstractEvent.h
  3. //
  4. // $Id: //poco/1.4/Foundation/include/Poco/AbstractEvent.h#1 $
  5. //
  6. // Library: Foundation
  7. // Package: Events
  8. // Module: AbstractEvent
  9. //
  10. // Definition of the AbstractEvent class.
  11. //
  12. // Copyright (c) 2006, Applied Informatics Software Engineering GmbH.
  13. // and Contributors.
  14. //
  15. // Permission is hereby granted, free of charge, to any person or organization
  16. // obtaining a copy of the software and accompanying documentation covered by
  17. // this license (the "Software") to use, reproduce, display, distribute,
  18. // execute, and transmit the Software, and to prepare derivative works of the
  19. // Software, and to permit third-parties to whom the Software is furnished to
  20. // do so, all subject to the following:
  21. //
  22. // The copyright notices in the Software and this entire statement, including
  23. // the above license grant, this restriction and the following disclaimer,
  24. // must be included in all copies of the Software, in whole or in part, and
  25. // all derivative works of the Software, unless such copies or derivative
  26. // works are solely in the form of machine-executable object code generated by
  27. // a source language processor.
  28. //
  29. // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
  30. // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
  31. // FITNESS FOR A PARTICULAR PURPOSE, TITLE AND NON-INFRINGEMENT. IN NO EVENT
  32. // SHALL THE COPYRIGHT HOLDERS OR ANYONE DISTRIBUTING THE SOFTWARE BE LIABLE
  33. // FOR ANY DAMAGES OR OTHER LIABILITY, WHETHER IN CONTRACT, TORT OR OTHERWISE,
  34. // ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
  35. // DEALINGS IN THE SOFTWARE.
  36. //
  37. #ifndef Foundation_AbstractFoundation_INCLUDED
  38. #define Foundation_AbstractFoundation_INCLUDED
  39. #include "Poco/Foundation.h"
  40. #include "Poco/SingletonHolder.h"
  41. #include "Poco/SharedPtr.h"
  42. #include "Poco/ActiveResult.h"
  43. #include "Poco/ActiveMethod.h"
  44. #include "Poco/Mutex.h"
  45. namespace Poco {
  46. template <class TArgs, class TStrategy, class TDelegate, class TMutex = FastMutex>
  47. class AbstractEvent
  48. /// An AbstractEvent is the super-class of all events.
  49. /// It works similar to the way C# handles notifications (aka events in C#).
  50. /// Events can be used to send information to a set of observers
  51. /// which are registered at the event. The type of the data is specified with
  52. /// the template parameter TArgs. The TStrategy parameter must be a subclass
  53. /// of NotificationStrategy. The parameter TDelegate can either be a subclass of AbstractDelegate
  54. /// or of PriorityAbstractDelegate.
  55. ///
  56. /// Note that AbstractEvent should never be used directly. One ought to use
  57. /// one of its subclasses which set the TStrategy and TDelegate template parameters
  58. /// to fixed values. For most use-cases the BasicEvent template will be sufficient:
  59. /// #include "Poco/BasicEvent.h"
  60. /// #include "Poco/Delegate.h"
  61. ///
  62. /// If one requires delegates to be called in the order they registered, use FIFOEvent:
  63. /// #include "Poco/FIFOEvent.h"
  64. /// #include "Poco/Delegate.h"
  65. ///
  66. /// Both FIFOEvent and BasicEvent work with a standard delegate. They allow one object to register
  67. /// exactly one delegate at an event. In contrast, a PriorityDelegate comes with an attached priority value
  68. /// and allows one object to register for one priority value one delegate. Note that PriorityDelegates
  69. /// only work with PriorityEvents:
  70. /// #include "Poco/PriorityEvent.h"
  71. /// #include "Poco/PriorityDelegate.h"
  72. ///
  73. /// Use events by adding them as public members to the object which is throwing notifications:
  74. ///
  75. /// class MyData
  76. /// {
  77. /// public:
  78. /// Poco::BasicEvent<int> AgeChanged;
  79. ///
  80. /// MyData();
  81. /// ...
  82. /// };
  83. ///
  84. /// Throwing the event can be done either by the events notify() or notifyAsync() method:
  85. ///
  86. ///
  87. /// Alternatively, instead of notify(), operator () can be used.
  88. ///
  89. /// void MyData::setAge(int i)
  90. /// {
  91. /// this->_age = i;
  92. /// AgeChanged(this, this->_age);
  93. /// }
  94. ///
  95. /// Note that notify and notifyAsync do not catch exceptions, i.e. in case a delegate
  96. /// throws an exception, the notify is immediately aborted and the exception is thrown
  97. /// back to the caller.
  98. ///
  99. /// Delegates can register methods at the event. In the case of a BasicEvent or FIFOEvent
  100. /// the Delegate template is used, in case of an PriorityEvent a PriorityDelegate is used.
  101. /// Mixing of observers, e.g. using a PriorityDelegate with a BasicEvent is not possible and
  102. /// checked for during compile time.
  103. /// Events require the observers to follow one of the following method signature:
  104. ///
  105. /// void onEvent(const void* pSender, TArgs& args);
  106. /// void onEvent(TArgs& args);
  107. /// static void onEvent(const void* pSender, TArgs& args);
  108. /// static void onEvent(void* pSender, TArgs& args);
  109. /// static void onEvent(TArgs& args);
  110. ///
  111. /// For performance reasons arguments are always sent by reference. This also allows observers
  112. /// to modify the sent argument. To prevent that, use <const TArg> as template
  113. /// parameter. A non-conformant method signature leads to compile errors.
  114. ///
  115. /// Assuming that the observer meets the method signature requirement, it can register
  116. /// this method with the += operator:
  117. ///
  118. /// class MyController
  119. /// {
  120. /// protected:
  121. /// MyData _data;
  122. ///
  123. /// void onDataChanged(void* pSender, int& data);
  124. /// ...
  125. /// };
  126. ///
  127. /// MyController::MyController()
  128. /// {
  129. /// _data.AgeChanged += delegate(this, &MyController::onDataChanged);
  130. /// }
  131. ///
  132. /// In some cases it might be desirable to work with automatically expiring registrations. Simply add
  133. /// to delegate as 3rd parameter a expireValue (in milliseconds):
  134. ///
  135. /// _data.DataChanged += delegate(this, &MyController::onDataChanged, 1000);
  136. ///
  137. /// This will add a delegate to the event which will automatically be removed in 1000 millisecs.
  138. ///
  139. /// Unregistering happens via the -= operator. Forgetting to unregister a method will lead to
  140. /// segmentation faults later, when one tries to send a notify to a no longer existing object.
  141. ///
  142. /// MyController::~MyController()
  143. /// {
  144. /// _data.DataChanged -= delegate(this, &MyController::onDataChanged);
  145. /// }
  146. ///
  147. /// Working with PriorityDelegates as similar to working with BasicEvent/FIFOEvent.Instead of ''delegate''
  148. /// simply use ''priorityDelegate''.
  149. ///
  150. /// For further examples refer to the event testsuites.
  151. {
  152. public:
  153. AbstractEvent():
  154. _executeAsync(this, &AbstractEvent::executeAsyncImpl),
  155. _enabled(true)
  156. {
  157. }
  158. AbstractEvent(const TStrategy& strat):
  159. _executeAsync(this, &AbstractEvent::executeAsyncImpl),
  160. _strategy(strat),
  161. _enabled(true)
  162. {
  163. }
  164. virtual ~AbstractEvent()
  165. {
  166. }
  167. void operator += (const TDelegate& aDelegate)
  168. /// Adds a delegate to the event. If the observer is equal to an
  169. /// already existing one (determined by the < operator),
  170. /// it will simply replace the existing observer.
  171. /// This behavior is determined by the TStrategy. Current implementations
  172. /// (DefaultStrategy, FIFOStrategy) follow that guideline but future ones
  173. /// can deviate.
  174. {
  175. typename TMutex::ScopedLock lock(_mutex);
  176. _strategy.add(aDelegate);
  177. }
  178. void operator -= (const TDelegate& aDelegate)
  179. /// Removes a delegate from the event. If the delegate is equal to an
  180. /// already existing one is determined by the < operator.
  181. /// If the observer is not found, the unregister will be ignored
  182. {
  183. typename TMutex::ScopedLock lock(_mutex);
  184. _strategy.remove(aDelegate);
  185. }
  186. void operator () (const void* pSender, TArgs& args)
  187. {
  188. notify(pSender, args);
  189. }
  190. void notify(const void* pSender, TArgs& args)
  191. /// Sends a notification to all registered delegates. The order is
  192. /// determined by the TStrategy. This method is blocking. While executing,
  193. /// other objects can change the list of delegates. These changes don't
  194. /// influence the current active notifications but are activated with
  195. /// the next notify. If one of the delegates throws an exception, the notify
  196. /// method is immediately aborted and the exception is reported to the caller.
  197. {
  198. SharedPtr<TStrategy> ptrStrat;
  199. bool enabled = false;
  200. {
  201. typename TMutex::ScopedLock lock(_mutex);
  202. enabled = _enabled;
  203. if (_enabled)
  204. {
  205. // thread-safeness:
  206. // copy should be faster and safer than blocking until
  207. // execution ends
  208. ptrStrat = new TStrategy(_strategy);
  209. }
  210. }
  211. if (enabled)
  212. {
  213. ptrStrat->notify(pSender, args);
  214. }
  215. }
  216. ActiveResult<TArgs> notifyAsync(const void* pSender, const TArgs& args)
  217. /// Sends a notification to all registered delegates. The order is
  218. /// determined by the TStrategy. This method is not blocking and will
  219. /// immediately return. The delegates are invoked in a seperate thread.
  220. /// Call activeResult.wait() to wait until the notification has ended.
  221. /// While executing, other objects can change the delegate list. These changes don't
  222. /// influence the current active notifications but are activated with
  223. /// the next notify. If one of the delegates throws an exception, the execution
  224. /// is aborted and the exception is reported to the caller.
  225. {
  226. NotifyAsyncParams params(pSender, args);
  227. {
  228. typename TMutex::ScopedLock lock(_mutex);
  229. // thread-safeness:
  230. // copy should be faster and safer than blocking until
  231. // execution ends
  232. // make a copy of the strategy here to guarantee that
  233. // between notifyAsync and the execution of the method no changes can occur
  234. params.ptrStrat = SharedPtr<TStrategy>(new TStrategy(_strategy));
  235. params.enabled = _enabled;
  236. }
  237. ActiveResult<TArgs> result = _executeAsync(params);
  238. return result;
  239. }
  240. void enable()
  241. /// Enables the event.
  242. {
  243. typename TMutex::ScopedLock lock(_mutex);
  244. _enabled = true;
  245. }
  246. void disable()
  247. /// Disables the event. notify and notifyAsnyc will be ignored,
  248. /// but adding/removing delegates is still allowed.
  249. {
  250. typename TMutex::ScopedLock lock(_mutex);
  251. _enabled = false;
  252. }
  253. bool isEnabled() const
  254. {
  255. typename TMutex::ScopedLock lock(_mutex);
  256. return _enabled;
  257. }
  258. void clear()
  259. /// Removes all delegates.
  260. {
  261. typename TMutex::ScopedLock lock(_mutex);
  262. _strategy.clear();
  263. }
  264. bool empty() const
  265. /// Checks if any delegates are registered at the delegate.
  266. {
  267. typename TMutex::ScopedLock lock(_mutex);
  268. return _strategy.empty();
  269. }
  270. protected:
  271. struct NotifyAsyncParams
  272. {
  273. SharedPtr<TStrategy> ptrStrat;
  274. const void* pSender;
  275. TArgs args;
  276. bool enabled;
  277. NotifyAsyncParams(const void* pSend, const TArgs& a):ptrStrat(), pSender(pSend), args(a), enabled(true)
  278. /// default constructor reduces the need for TArgs to have an empty constructor, only copy constructor is needed.
  279. {
  280. }
  281. };
  282. ActiveMethod<TArgs, NotifyAsyncParams, AbstractEvent> _executeAsync;
  283. TArgs executeAsyncImpl(const NotifyAsyncParams& par)
  284. {
  285. if (!par.enabled)
  286. {
  287. return par.args;
  288. }
  289. NotifyAsyncParams params = par;
  290. TArgs retArgs(params.args);
  291. params.ptrStrat->notify(params.pSender, retArgs);
  292. return retArgs;
  293. }
  294. TStrategy _strategy; /// The strategy used to notify observers.
  295. bool _enabled; /// Stores if an event is enabled. Notfies on disabled events have no effect
  296. /// but it is possible to change the observers.
  297. mutable TMutex _mutex;
  298. private:
  299. AbstractEvent(const AbstractEvent& other);
  300. AbstractEvent& operator = (const AbstractEvent& other);
  301. };
  302. } // namespace Poco
  303. #endif // Foundation_AbstractFoundation_INCLUDED