1 | // |
---|
2 | // AbstractEvent.h |
---|
3 | // |
---|
4 | // $Id: //poco/1.3/Foundation/include/Poco/AbstractEvent.h#2 $ |
---|
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 | |
---|
38 | |
---|
39 | #ifndef Foundation_AbstractFoundation_INCLUDED |
---|
40 | #define Foundation_AbstractFoundation_INCLUDED |
---|
41 | |
---|
42 | |
---|
43 | #include "Poco/Foundation.h" |
---|
44 | #include "Poco/SingletonHolder.h" |
---|
45 | #include "Poco/SharedPtr.h" |
---|
46 | #include "Poco/ActiveResult.h" |
---|
47 | #include "Poco/ActiveMethod.h" |
---|
48 | #include "Poco/Mutex.h" |
---|
49 | |
---|
50 | |
---|
51 | namespace Poco { |
---|
52 | |
---|
53 | |
---|
54 | template <class TArgs, class TStrategy, class TDelegate> |
---|
55 | class AbstractEvent |
---|
56 | /// An AbstractEvent is the super-class of all events. |
---|
57 | /// It works similar to the way C# handles notifications (aka events in C#). |
---|
58 | /// Events can be used to send information to a set of observers |
---|
59 | /// which are registered at the event. The type of the data is specified with |
---|
60 | /// the template parameter TArgs. The TStrategy parameter must be a subclass |
---|
61 | /// of NotificationStrategy. The parameter TDelegate can either be a subclass of AbstractDelegate |
---|
62 | /// or of PriorityAbstractDelegate. |
---|
63 | /// |
---|
64 | /// Note that AbstractEvent should never be used directly. One ought to use |
---|
65 | /// one of its subclasses which set the TStrategy and TDelegate template parameters |
---|
66 | /// to fixed values. For most use-cases the BasicEvent template will be sufficient: |
---|
67 | /// #include "Poco/BasicEvent.h" |
---|
68 | /// #include "Poco/Delegate.h" |
---|
69 | /// |
---|
70 | /// If one requires delegates to be called in the order they registered, use FIFOEvent: |
---|
71 | /// #include "Poco/FIFOEvent.h" |
---|
72 | /// #include "Poco/Delegate.h" |
---|
73 | /// |
---|
74 | /// Both FIFOEvent and BasicEvent work with a standard delegate. They allow one object to register |
---|
75 | /// exactly one delegate at an event. In contrast, a PriorityDelegate comes with an attached priority value |
---|
76 | /// and allows one object to register for one priority value one delegate. Note that PriorityDelegates |
---|
77 | /// only work with PriorityEvents: |
---|
78 | /// #include "Poco/PriorityEvent.h" |
---|
79 | /// #include "Poco/PriorityDelegate.h" |
---|
80 | /// |
---|
81 | /// Use events by adding them as public members to the object which is throwing notifications: |
---|
82 | /// |
---|
83 | /// class MyData |
---|
84 | /// { |
---|
85 | /// public: |
---|
86 | /// Poco::BasicEvent<int> AgeChanged; |
---|
87 | /// |
---|
88 | /// MyData(); |
---|
89 | /// ... |
---|
90 | /// }; |
---|
91 | /// |
---|
92 | /// Throwing the event can be done either by the events notify() or notifyAsync() method: |
---|
93 | /// |
---|
94 | /// |
---|
95 | /// Alternatively, instead of notify(), operator () can be used. |
---|
96 | /// |
---|
97 | /// void MyData::setAge(int i) |
---|
98 | /// { |
---|
99 | /// this->_age = i; |
---|
100 | /// AgeChanged(this, this->_age); |
---|
101 | /// } |
---|
102 | /// |
---|
103 | /// Note that notify and notifyAsync do not catch exceptions, i.e. in case a delegate |
---|
104 | /// throws an exception, the notify is immediately aborted and the exception is thrown |
---|
105 | /// back to the caller. |
---|
106 | /// |
---|
107 | /// Delegates can register methods at the event. In the case of a BasicEvent or FIFOEvent |
---|
108 | /// the Delegate template is used, in case of an PriorityEvent a PriorityDelegate is used. |
---|
109 | /// Mixing of observers, e.g. using a PriorityDelegate with a BasicEvent is not possible and |
---|
110 | /// checked for during compile time. |
---|
111 | /// Events require the observers to follow one of the following method signature: |
---|
112 | /// |
---|
113 | /// void onEvent(const void* pSender, TArgs& args); |
---|
114 | /// void onEvent(TArgs& args); |
---|
115 | /// static void onEvent(const void* pSender, TArgs& args); |
---|
116 | /// static void onEvent(void* pSender, TArgs& args); |
---|
117 | /// static void onEvent(TArgs& args); |
---|
118 | /// |
---|
119 | /// For performance reasons arguments are always sent by reference. This also allows observers |
---|
120 | /// to modify the sent argument. To prevent that, use <const TArg> as template |
---|
121 | /// parameter. A non-conformant method signature leads to compile errors. |
---|
122 | /// |
---|
123 | /// Assuming that the observer meets the method signature requirement, it can register |
---|
124 | /// this method with the += operator: |
---|
125 | /// |
---|
126 | /// class MyController |
---|
127 | /// { |
---|
128 | /// protected: |
---|
129 | /// MyData _data; |
---|
130 | /// |
---|
131 | /// void onDataChanged(void* pSender, int& data); |
---|
132 | /// ... |
---|
133 | /// }; |
---|
134 | /// |
---|
135 | /// MyController::MyController() |
---|
136 | /// { |
---|
137 | /// _data.AgeChanged += delegate(this, &MyController::onDataChanged); |
---|
138 | /// } |
---|
139 | /// |
---|
140 | /// In some cases it might be desirable to work with automatically expiring registrations. Simply add |
---|
141 | /// to delegate as 3rd parameter a expireValue (in milliseconds): |
---|
142 | /// |
---|
143 | /// _data.DataChanged += delegate(this, &MyController::onDataChanged, 1000); |
---|
144 | /// |
---|
145 | /// This will add a delegate to the event which will automatically be removed in 1000 millisecs. |
---|
146 | /// |
---|
147 | /// Unregistering happens via the -= operator. Forgetting to unregister a method will lead to |
---|
148 | /// segmentation faults later, when one tries to send a notify to a no longer existing object. |
---|
149 | /// |
---|
150 | /// MyController::~MyController() |
---|
151 | /// { |
---|
152 | /// _data.DataChanged -= delegate(this, &MyController::onDataChanged); |
---|
153 | /// } |
---|
154 | /// |
---|
155 | /// Working with PriorityDelegates as similar to working with BasicEvent/FIFOEvent.Instead of ''delegate'' |
---|
156 | /// simply use ''priorityDelegate''. |
---|
157 | /// |
---|
158 | /// For further examples refer to the event testsuites. |
---|
159 | { |
---|
160 | public: |
---|
161 | AbstractEvent(): |
---|
162 | _executeAsync(this, &AbstractEvent::executeAsyncImpl), |
---|
163 | _enabled(true) |
---|
164 | { |
---|
165 | } |
---|
166 | |
---|
167 | AbstractEvent(const TStrategy& strat): |
---|
168 | _executeAsync(this, &AbstractEvent::executeAsyncImpl), |
---|
169 | _strategy(strat), |
---|
170 | _enabled(true) |
---|
171 | { |
---|
172 | } |
---|
173 | |
---|
174 | virtual ~AbstractEvent() |
---|
175 | { |
---|
176 | } |
---|
177 | |
---|
178 | void operator += (const TDelegate& aDelegate) |
---|
179 | /// Adds a delegate to the event. If the observer is equal to an |
---|
180 | /// already existing one (determined by the < operator), |
---|
181 | /// it will simply replace the existing observer. |
---|
182 | /// This behavior is determined by the TStrategy. Current implementations |
---|
183 | /// (DefaultStrategy, FIFOStrategy) follow that guideline but future ones |
---|
184 | /// can deviate. |
---|
185 | { |
---|
186 | FastMutex::ScopedLock lock(_mutex); |
---|
187 | _strategy.add(aDelegate); |
---|
188 | } |
---|
189 | |
---|
190 | void operator -= (const TDelegate& aDelegate) |
---|
191 | /// Removes a delegate from the event. If the delegate is equal to an |
---|
192 | /// already existing one is determined by the < operator. |
---|
193 | /// If the observer is not found, the unregister will be ignored |
---|
194 | { |
---|
195 | FastMutex::ScopedLock lock(_mutex); |
---|
196 | _strategy.remove(aDelegate); |
---|
197 | } |
---|
198 | |
---|
199 | void operator () (const void* pSender, TArgs& args) |
---|
200 | { |
---|
201 | notify(pSender, args); |
---|
202 | } |
---|
203 | |
---|
204 | void notify(const void* pSender, TArgs& args) |
---|
205 | /// Sends a notification to all registered delegates. The order is |
---|
206 | /// determined by the TStrategy. This method is blocking. While executing, |
---|
207 | /// other objects can change the list of delegates. These changes don't |
---|
208 | /// influence the current active notifications but are activated with |
---|
209 | /// the next notify. If one of the delegates throws an exception, the notify |
---|
210 | /// method is immediately aborted and the exception is reported to the caller. |
---|
211 | { |
---|
212 | SharedPtr<TStrategy> ptrStrat; |
---|
213 | bool enabled = false; |
---|
214 | |
---|
215 | { |
---|
216 | FastMutex::ScopedLock lock(_mutex); |
---|
217 | enabled = _enabled; |
---|
218 | if (_enabled) |
---|
219 | { |
---|
220 | // thread-safeness: |
---|
221 | // copy should be faster and safer than blocking until |
---|
222 | // execution ends |
---|
223 | ptrStrat = new TStrategy(_strategy); |
---|
224 | } |
---|
225 | } |
---|
226 | |
---|
227 | if (enabled) |
---|
228 | { |
---|
229 | ptrStrat->notify(pSender, args); |
---|
230 | } |
---|
231 | } |
---|
232 | |
---|
233 | ActiveResult<TArgs> notifyAsync(const void* pSender, const TArgs& args) |
---|
234 | /// Sends a notification to all registered delegates. The order is |
---|
235 | /// determined by the TStrategy. This method is not blocking and will |
---|
236 | /// immediately return. The delegates are invoked in a seperate thread. |
---|
237 | /// Call activeResult.wait() to wait until the notification has ended. |
---|
238 | /// While executing, other objects can change the delegate list. These changes don't |
---|
239 | /// influence the current active notifications but are activated with |
---|
240 | /// the next notify. If one of the delegates throws an exception, the execution |
---|
241 | /// is aborted and the exception is reported to the caller. |
---|
242 | { |
---|
243 | NotifyAsyncParams params(pSender, args); |
---|
244 | |
---|
245 | { |
---|
246 | FastMutex::ScopedLock lock(_mutex); |
---|
247 | |
---|
248 | // thread-safeness: |
---|
249 | // copy should be faster and safer than blocking until |
---|
250 | // execution ends |
---|
251 | // make a copy of the strategy here to guarantee that |
---|
252 | // between notifyAsync and the execution of the method no changes can occur |
---|
253 | |
---|
254 | params.ptrStrat = SharedPtr<TStrategy>(new TStrategy(_strategy)); |
---|
255 | params.enabled = _enabled; |
---|
256 | } |
---|
257 | |
---|
258 | ActiveResult<TArgs> result = _executeAsync(params); |
---|
259 | return result; |
---|
260 | } |
---|
261 | |
---|
262 | void enable() |
---|
263 | /// Enables the event |
---|
264 | { |
---|
265 | FastMutex::ScopedLock lock(_mutex); |
---|
266 | _enabled = true; |
---|
267 | } |
---|
268 | |
---|
269 | void disable() |
---|
270 | /// Disables the event. notify and notifyAsnyc will be ignored, |
---|
271 | /// but adding/removing delegates is still allowed. |
---|
272 | { |
---|
273 | FastMutex::ScopedLock lock(_mutex); |
---|
274 | _enabled = false; |
---|
275 | } |
---|
276 | |
---|
277 | bool isEnabled() const |
---|
278 | { |
---|
279 | FastMutex::ScopedLock lock(_mutex); |
---|
280 | return _enabled; |
---|
281 | } |
---|
282 | |
---|
283 | void clear() |
---|
284 | /// Removes all delegates. |
---|
285 | { |
---|
286 | FastMutex::ScopedLock lock(_mutex); |
---|
287 | _strategy.clear(); |
---|
288 | } |
---|
289 | |
---|
290 | protected: |
---|
291 | struct NotifyAsyncParams |
---|
292 | { |
---|
293 | SharedPtr<TStrategy> ptrStrat; |
---|
294 | const void* pSender; |
---|
295 | TArgs args; |
---|
296 | bool enabled; |
---|
297 | |
---|
298 | NotifyAsyncParams(const void* pSend, const TArgs& a):ptrStrat(), pSender(pSend), args(a), enabled(true) |
---|
299 | /// default constructor reduces the need for TArgs to have an empty constructor, only copy constructor is needed. |
---|
300 | { |
---|
301 | } |
---|
302 | }; |
---|
303 | |
---|
304 | ActiveMethod<TArgs, NotifyAsyncParams, AbstractEvent> _executeAsync; |
---|
305 | |
---|
306 | TArgs executeAsyncImpl(const NotifyAsyncParams& par) |
---|
307 | { |
---|
308 | if (!par.enabled) |
---|
309 | { |
---|
310 | return par.args; |
---|
311 | } |
---|
312 | |
---|
313 | NotifyAsyncParams params = par; |
---|
314 | TArgs retArgs(params.args); |
---|
315 | params.ptrStrat->notify(params.pSender, retArgs); |
---|
316 | return retArgs; |
---|
317 | } |
---|
318 | |
---|
319 | TStrategy _strategy; /// The strategy used to notify observers. |
---|
320 | bool _enabled; /// Stores if an event is enabled. Notfies on disabled events have no effect |
---|
321 | /// but it is possible to change the observers. |
---|
322 | mutable FastMutex _mutex; |
---|
323 | |
---|
324 | private: |
---|
325 | AbstractEvent(const AbstractEvent& other); |
---|
326 | AbstractEvent& operator = (const AbstractEvent& other); |
---|
327 | }; |
---|
328 | |
---|
329 | |
---|
330 | } // namespace Poco |
---|
331 | |
---|
332 | |
---|
333 | #endif |
---|