/libs/poco/include/Poco/AbstractEvent.h
C Header | 340 lines | 126 code | 37 blank | 177 comment | 3 complexity | 37587c4cd4788825ecc5933a7a03866e MD5 | raw file
- //
- // AbstractEvent.h
- //
- // $Id: //poco/1.4/Foundation/include/Poco/AbstractEvent.h#1 $
- //
- // Library: Foundation
- // Package: Events
- // Module: AbstractEvent
- //
- // Definition of the AbstractEvent class.
- //
- // Copyright (c) 2006, Applied Informatics Software Engineering GmbH.
- // and Contributors.
- //
- // Permission is hereby granted, free of charge, to any person or organization
- // obtaining a copy of the software and accompanying documentation covered by
- // this license (the "Software") to use, reproduce, display, distribute,
- // execute, and transmit the Software, and to prepare derivative works of the
- // Software, and to permit third-parties to whom the Software is furnished to
- // do so, all subject to the following:
- //
- // The copyright notices in the Software and this entire statement, including
- // the above license grant, this restriction and the following disclaimer,
- // must be included in all copies of the Software, in whole or in part, and
- // all derivative works of the Software, unless such copies or derivative
- // works are solely in the form of machine-executable object code generated by
- // a source language processor.
- //
- // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
- // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
- // FITNESS FOR A PARTICULAR PURPOSE, TITLE AND NON-INFRINGEMENT. IN NO EVENT
- // SHALL THE COPYRIGHT HOLDERS OR ANYONE DISTRIBUTING THE SOFTWARE BE LIABLE
- // FOR ANY DAMAGES OR OTHER LIABILITY, WHETHER IN CONTRACT, TORT OR OTHERWISE,
- // ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
- // DEALINGS IN THE SOFTWARE.
- //
- #ifndef Foundation_AbstractFoundation_INCLUDED
- #define Foundation_AbstractFoundation_INCLUDED
- #include "Poco/Foundation.h"
- #include "Poco/SingletonHolder.h"
- #include "Poco/SharedPtr.h"
- #include "Poco/ActiveResult.h"
- #include "Poco/ActiveMethod.h"
- #include "Poco/Mutex.h"
- namespace Poco {
- template <class TArgs, class TStrategy, class TDelegate, class TMutex = FastMutex>
- class AbstractEvent
- /// An AbstractEvent is the super-class of all events.
- /// It works similar to the way C# handles notifications (aka events in C#).
- /// Events can be used to send information to a set of observers
- /// which are registered at the event. The type of the data is specified with
- /// the template parameter TArgs. The TStrategy parameter must be a subclass
- /// of NotificationStrategy. The parameter TDelegate can either be a subclass of AbstractDelegate
- /// or of PriorityAbstractDelegate.
- ///
- /// Note that AbstractEvent should never be used directly. One ought to use
- /// one of its subclasses which set the TStrategy and TDelegate template parameters
- /// to fixed values. For most use-cases the BasicEvent template will be sufficient:
- /// #include "Poco/BasicEvent.h"
- /// #include "Poco/Delegate.h"
- ///
- /// If one requires delegates to be called in the order they registered, use FIFOEvent:
- /// #include "Poco/FIFOEvent.h"
- /// #include "Poco/Delegate.h"
- ///
- /// Both FIFOEvent and BasicEvent work with a standard delegate. They allow one object to register
- /// exactly one delegate at an event. In contrast, a PriorityDelegate comes with an attached priority value
- /// and allows one object to register for one priority value one delegate. Note that PriorityDelegates
- /// only work with PriorityEvents:
- /// #include "Poco/PriorityEvent.h"
- /// #include "Poco/PriorityDelegate.h"
- ///
- /// Use events by adding them as public members to the object which is throwing notifications:
- ///
- /// class MyData
- /// {
- /// public:
- /// Poco::BasicEvent<int> AgeChanged;
- ///
- /// MyData();
- /// ...
- /// };
- ///
- /// Throwing the event can be done either by the events notify() or notifyAsync() method:
- ///
- ///
- /// Alternatively, instead of notify(), operator () can be used.
- ///
- /// void MyData::setAge(int i)
- /// {
- /// this->_age = i;
- /// AgeChanged(this, this->_age);
- /// }
- ///
- /// Note that notify and notifyAsync do not catch exceptions, i.e. in case a delegate
- /// throws an exception, the notify is immediately aborted and the exception is thrown
- /// back to the caller.
- ///
- /// Delegates can register methods at the event. In the case of a BasicEvent or FIFOEvent
- /// the Delegate template is used, in case of an PriorityEvent a PriorityDelegate is used.
- /// Mixing of observers, e.g. using a PriorityDelegate with a BasicEvent is not possible and
- /// checked for during compile time.
- /// Events require the observers to follow one of the following method signature:
- ///
- /// void onEvent(const void* pSender, TArgs& args);
- /// void onEvent(TArgs& args);
- /// static void onEvent(const void* pSender, TArgs& args);
- /// static void onEvent(void* pSender, TArgs& args);
- /// static void onEvent(TArgs& args);
- ///
- /// For performance reasons arguments are always sent by reference. This also allows observers
- /// to modify the sent argument. To prevent that, use <const TArg> as template
- /// parameter. A non-conformant method signature leads to compile errors.
- ///
- /// Assuming that the observer meets the method signature requirement, it can register
- /// this method with the += operator:
- ///
- /// class MyController
- /// {
- /// protected:
- /// MyData _data;
- ///
- /// void onDataChanged(void* pSender, int& data);
- /// ...
- /// };
- ///
- /// MyController::MyController()
- /// {
- /// _data.AgeChanged += delegate(this, &MyController::onDataChanged);
- /// }
- ///
- /// In some cases it might be desirable to work with automatically expiring registrations. Simply add
- /// to delegate as 3rd parameter a expireValue (in milliseconds):
- ///
- /// _data.DataChanged += delegate(this, &MyController::onDataChanged, 1000);
- ///
- /// This will add a delegate to the event which will automatically be removed in 1000 millisecs.
- ///
- /// Unregistering happens via the -= operator. Forgetting to unregister a method will lead to
- /// segmentation faults later, when one tries to send a notify to a no longer existing object.
- ///
- /// MyController::~MyController()
- /// {
- /// _data.DataChanged -= delegate(this, &MyController::onDataChanged);
- /// }
- ///
- /// Working with PriorityDelegates as similar to working with BasicEvent/FIFOEvent.Instead of ''delegate''
- /// simply use ''priorityDelegate''.
- ///
- /// For further examples refer to the event testsuites.
- {
- public:
- AbstractEvent():
- _executeAsync(this, &AbstractEvent::executeAsyncImpl),
- _enabled(true)
- {
- }
- AbstractEvent(const TStrategy& strat):
- _executeAsync(this, &AbstractEvent::executeAsyncImpl),
- _strategy(strat),
- _enabled(true)
- {
- }
- virtual ~AbstractEvent()
- {
- }
- void operator += (const TDelegate& aDelegate)
- /// Adds a delegate to the event. If the observer is equal to an
- /// already existing one (determined by the < operator),
- /// it will simply replace the existing observer.
- /// This behavior is determined by the TStrategy. Current implementations
- /// (DefaultStrategy, FIFOStrategy) follow that guideline but future ones
- /// can deviate.
- {
- typename TMutex::ScopedLock lock(_mutex);
- _strategy.add(aDelegate);
- }
-
- void operator -= (const TDelegate& aDelegate)
- /// Removes a delegate from the event. If the delegate is equal to an
- /// already existing one is determined by the < operator.
- /// If the observer is not found, the unregister will be ignored
- {
- typename TMutex::ScopedLock lock(_mutex);
- _strategy.remove(aDelegate);
- }
-
- void operator () (const void* pSender, TArgs& args)
- {
- notify(pSender, args);
- }
- void notify(const void* pSender, TArgs& args)
- /// Sends a notification to all registered delegates. The order is
- /// determined by the TStrategy. This method is blocking. While executing,
- /// other objects can change the list of delegates. These changes don't
- /// influence the current active notifications but are activated with
- /// the next notify. If one of the delegates throws an exception, the notify
- /// method is immediately aborted and the exception is reported to the caller.
- {
- SharedPtr<TStrategy> ptrStrat;
- bool enabled = false;
-
- {
- typename TMutex::ScopedLock lock(_mutex);
- enabled = _enabled;
- if (_enabled)
- {
- // thread-safeness:
- // copy should be faster and safer than blocking until
- // execution ends
- ptrStrat = new TStrategy(_strategy);
- }
- }
- if (enabled)
- {
- ptrStrat->notify(pSender, args);
- }
- }
- ActiveResult<TArgs> notifyAsync(const void* pSender, const TArgs& args)
- /// Sends a notification to all registered delegates. The order is
- /// determined by the TStrategy. This method is not blocking and will
- /// immediately return. The delegates are invoked in a seperate thread.
- /// Call activeResult.wait() to wait until the notification has ended.
- /// While executing, other objects can change the delegate list. These changes don't
- /// influence the current active notifications but are activated with
- /// the next notify. If one of the delegates throws an exception, the execution
- /// is aborted and the exception is reported to the caller.
- {
- NotifyAsyncParams params(pSender, args);
- {
- typename TMutex::ScopedLock lock(_mutex);
- // thread-safeness:
- // copy should be faster and safer than blocking until
- // execution ends
- // make a copy of the strategy here to guarantee that
- // between notifyAsync and the execution of the method no changes can occur
-
- params.ptrStrat = SharedPtr<TStrategy>(new TStrategy(_strategy));
- params.enabled = _enabled;
- }
- ActiveResult<TArgs> result = _executeAsync(params);
- return result;
- }
-
- void enable()
- /// Enables the event.
- {
- typename TMutex::ScopedLock lock(_mutex);
- _enabled = true;
- }
- void disable()
- /// Disables the event. notify and notifyAsnyc will be ignored,
- /// but adding/removing delegates is still allowed.
- {
- typename TMutex::ScopedLock lock(_mutex);
- _enabled = false;
- }
- bool isEnabled() const
- {
- typename TMutex::ScopedLock lock(_mutex);
- return _enabled;
- }
- void clear()
- /// Removes all delegates.
- {
- typename TMutex::ScopedLock lock(_mutex);
- _strategy.clear();
- }
-
- bool empty() const
- /// Checks if any delegates are registered at the delegate.
- {
- typename TMutex::ScopedLock lock(_mutex);
- return _strategy.empty();
- }
- protected:
- struct NotifyAsyncParams
- {
- SharedPtr<TStrategy> ptrStrat;
- const void* pSender;
- TArgs args;
- bool enabled;
-
- NotifyAsyncParams(const void* pSend, const TArgs& a):ptrStrat(), pSender(pSend), args(a), enabled(true)
- /// default constructor reduces the need for TArgs to have an empty constructor, only copy constructor is needed.
- {
- }
- };
- ActiveMethod<TArgs, NotifyAsyncParams, AbstractEvent> _executeAsync;
- TArgs executeAsyncImpl(const NotifyAsyncParams& par)
- {
- if (!par.enabled)
- {
- return par.args;
- }
- NotifyAsyncParams params = par;
- TArgs retArgs(params.args);
- params.ptrStrat->notify(params.pSender, retArgs);
- return retArgs;
- }
- TStrategy _strategy; /// The strategy used to notify observers.
- bool _enabled; /// Stores if an event is enabled. Notfies on disabled events have no effect
- /// but it is possible to change the observers.
- mutable TMutex _mutex;
- private:
- AbstractEvent(const AbstractEvent& other);
- AbstractEvent& operator = (const AbstractEvent& other);
- };
- } // namespace Poco
- #endif // Foundation_AbstractFoundation_INCLUDED