/* * Observable.hpp * * Created on: Dec 1, 2011 * Author: heber */ #ifndef OBSERVABLE_HPP_ #define OBSERVABLE_HPP_ // include config.h #ifdef HAVE_CONFIG_H #include #endif #include #include #include #include #include "CodePatterns/Range.hpp" #include "CodePatterns/Observer/defs.hpp" #include "CodePatterns/Observer/Observer.hpp" class Graveyard; /** * An Observable implements all neccessary method for being observed. * * That is, it provides methods for signing on and of from an * Observable that can be used by any observer. The actual * observer-mechanism is handled at a central static place * to avoid memory issues when many observable are around but only few * are actually observed. * * \note We have to clean our Channels from static NotificationChannels and * we call Channels::subjectKilled() to let Observers that have only signed * on to single channel still know when their observable has died. * * Note that one may allow an Observable to live some over-time by using * the Graveyard. This allows any Observer to still access the instance * in order to properly sign off. It is destroyed when no Observer is left. */ class Observable : public Observer { public: Observable(std::string _name); virtual ~Observable(); /** This class is only used to distinguish from size_t in the overload. * * It encapsulates a const int (the priority level) and checks valid bounds * in constructor. * */ class PriorityLevel { public: explicit PriorityLevel(const int i); ~PriorityLevel(); const int level; private: static range ValidRange; }; /** * Sign an Observer on to this Observable. The Observer will be notified * whenever something inside the Observable changes. The Observer can * assign itself a priority for the changes in the range of -20:+20. * The Observer with lower priority will be called before the others, * same as with Unix nice-levels. This can be used when an Object * contains other objects that observe it (derived values), and these objects have * to recalculate their states before the changes should be propageted to the * UI. A default priority of 0 should be fine in most cases, since there is * ussually no need to order the update sequence. */ virtual void signOn(Observer *target, PriorityLevel priority = PriorityDefault) const; /** * Sign of a previously signed on Observer. After this no more * updates will be recieved from that observer. */ virtual void signOff(Observer *target) const; /** * Sign on for specialized notifications */ virtual void signOn(Observer *target, size_t channelno) const; /** * Stop receiving a specialized notification */ virtual void signOff(Observer *target, size_t channelno) const; /** * Ask an Observer if it is currently in a blocked state, i.e. if * Changes are in Progress, that are not yet published. */ virtual bool isBlocked() const; Notification_ptr getChannel(size_t no) const; size_t getNumberOfObservers() const; protected: virtual void update(Observable *publisher); virtual void subjectKilled(Observable *publisher); virtual void notifyAll(); protected: // Observer mechanism is done from a static central place /** * Internal method. * Do not call directly. Use OBSERVE macro instead */ static void start_observer_internal(Observable *publisher); /** * Internal method. * Do not call directly. Use OBSERVE macro instead */ static void finish_observer_internal(Observable *publisher); static void enque_notification_internal(Observable *publisher, Notification_ptr notification); typedef std::map ChannelMap; static ChannelMap NotificationChannels; static PriorityLevel PriorityDefault; protected: typedef std::multimap callees_t; typedef std::set notificationSet; static std::map depth; static std::map callTable; static std::map notifications; static std::set busyObservables; private: friend class Zombie; friend class Graveyard; typedef boost::function graveyard_informer_t; /** Bound function to call when Observer are signing off (needs to be a ptr * as we must be able to rebound it. * * \warning Do not delete this pointer, the instance is either a static one * or handled someplace else (e.g. in the Graveyard). */ graveyard_informer_t * graveyard_informer; //!> default informer that does nothing static graveyard_informer_t noop_informer; /** Sets the bound function for over-time life-time management. * * \param _graveyard ptr Graveyard to inform of leaving Observers */ void setGraveyardInformer(graveyard_informer_t * _graveyard_informer) { graveyard_informer = _graveyard_informer; } //! @cond // Structure for RAII-Style notification public: /** * This structure implements the Observer-mechanism RAII-Idiom. * It triggers certain functions on creation and destruction so that * Observer mechanisms can be linked to scope block. */ class _Observable_protector { public: _Observable_protector(Observable *); _Observable_protector(const _Observable_protector&); ~_Observable_protector(); private: Observable *protege; }; //! @endcond }; // extra macro is necessary to work with __LINE__ #define PASTE(a,b) PASTE_HELPER(a,b) #define PASTE_HELPER(a,b) a ## b #define OBSERVE Observable::_Observable_protector PASTE(_scope_obs_protector_,__LINE__)(this) #define NOTIFY(channelno) do{Observable::enque_notification_internal(this,NotificationChannels[this]->getChannel(channelno));}while(0) #define LOCK_OBSERVABLE(observable) Observable::_Observable_protector PASTE(_scope_obs_protector_,__LINE__)(&(observable)) #endif /* OBSERVABLE_HPP_ */