source: src/CodePatterns/Observer/Observable.hpp@ 1b5188

Last change on this file since 1b5188 was 1b5188, checked in by Frederik Heber <heber@…>, 10 years ago

Observables are now protected by mutexes, i.e. allow for concurrent use.

  • Property mode set to 100644
File size: 6.0 KB
Line 
1/*
2 * Observable.hpp
3 *
4 * Created on: Dec 1, 2011
5 * Author: heber
6 */
7
8#ifndef OBSERVABLE_HPP_
9#define OBSERVABLE_HPP_
10
11// include config.h
12#ifdef HAVE_CONFIG_H
13#include <config.h>
14#endif
15
16#include <map>
17#include <set>
18#include <string>
19#include <boost/function.hpp>
20#include <boost/thread.hpp>
21
22#include "CodePatterns/Range.hpp"
23#include "CodePatterns/Observer/defs.hpp"
24#include "CodePatterns/Observer/Observer.hpp"
25
26class Graveyard;
27
28/**
29 * An Observable implements all neccessary method for being observed.
30 *
31 * That is, it provides methods for signing on and of from an
32 * Observable that can be used by any observer. The actual
33 * observer-mechanism is handled at a central static place
34 * to avoid memory issues when many observable are around but only few
35 * are actually observed.
36 *
37 * \note We have to clean our Channels from static NotificationChannels and
38 * we call Channels::subjectKilled() to let Observers that have only signed
39 * on to single channel still know when their observable has died.
40 *
41 * Note that one may allow an Observable to live some over-time by using
42 * the Graveyard. This allows any Observer to still access the instance
43 * in order to properly sign off. It is destroyed when no Observer is left.
44 */
45class Observable : public Observer {
46public:
47 Observable(std::string _name);
48 virtual ~Observable();
49
50 /** This class is only used to distinguish from size_t in the overload.
51 *
52 * It encapsulates a const int (the priority level) and checks valid bounds
53 * in constructor.
54 *
55 */
56 class PriorityLevel {
57 public:
58 explicit PriorityLevel(const int i);
59 ~PriorityLevel();
60
61 const int level;
62 private:
63 static range<int> ValidRange;
64 };
65
66 /**
67 * Sign an Observer on to this Observable. The Observer will be notified
68 * whenever something inside the Observable changes. The Observer can
69 * assign itself a priority for the changes in the range of -20:+20.
70 * The Observer with lower priority will be called before the others,
71 * same as with Unix nice-levels. This can be used when an Object
72 * contains other objects that observe it (derived values), and these objects have
73 * to recalculate their states before the changes should be propageted to the
74 * UI. A default priority of 0 should be fine in most cases, since there is
75 * ussually no need to order the update sequence.
76 */
77 virtual void signOn(Observer *target, PriorityLevel priority = PriorityDefault) const;
78
79 /**
80 * Sign of a previously signed on Observer. After this no more
81 * updates will be recieved from that observer.
82 */
83 virtual void signOff(Observer *target) const;
84
85 /**
86 * Sign on for specialized notifications
87 */
88 virtual void signOn(Observer *target, size_t channelno) const;
89
90 /**
91 * Stop receiving a specialized notification
92 */
93 virtual void signOff(Observer *target, size_t channelno) const;
94
95 /**
96 * Ask an Observer if it is currently in a blocked state, i.e. if
97 * Changes are in Progress, that are not yet published.
98 */
99 virtual bool isBlocked() const;
100
101 Notification_ptr getChannel(size_t no) const;
102
103 size_t getNumberOfObservers() const;
104
105protected:
106 virtual void update(Observable *publisher);
107 virtual void subjectKilled(Observable *publisher);
108
109 virtual void notifyAll();
110protected:
111// Observer mechanism is done from a static central place
112 /**
113 * Internal method.
114 * Do not call directly. Use OBSERVE macro instead
115 */
116 static void start_observer_internal(Observable *publisher);
117 /**
118 * Internal method.
119 * Do not call directly. Use OBSERVE macro instead
120 */
121 static void finish_observer_internal(Observable *publisher);
122
123 static void enque_notification_internal(Observable *publisher, Notification_ptr notification);
124
125 typedef std::map<Observable*, Channels *> ChannelMap;
126 static ChannelMap NotificationChannels;
127
128 static PriorityLevel PriorityDefault;
129
130protected:
131 typedef std::multimap<int,Observer*> callees_t;
132 typedef std::set<Notification*> notificationSet;
133 static std::map<Observable*, int> depth;
134 static std::map<Observable*,callees_t> callTable;
135 static std::map<Observable*,notificationSet> notifications;
136 static std::set<Observable*> busyObservables;
137
138 static boost::recursive_mutex ObservablesMapLock; //!< a lock for the pointer of the instance
139
140private:
141 friend class Zombie;
142 friend class Graveyard;
143
144 typedef boost::function<void (const Observable*)> graveyard_informer_t;
145
146 /** Bound function to call when Observer are signing off (needs to be a ptr
147 * as we must be able to rebound it.
148 *
149 * \warning Do not delete this pointer, the instance is either a static one
150 * or handled someplace else (e.g. in the Graveyard).
151 */
152 graveyard_informer_t * graveyard_informer;
153
154 //!> default informer that does nothing
155 static graveyard_informer_t noop_informer;
156
157 /** Sets the bound function for over-time life-time management.
158 *
159 * \param _graveyard ptr Graveyard to inform of leaving Observers
160 */
161 void setGraveyardInformer(graveyard_informer_t * _graveyard_informer)
162 {
163 graveyard_informer = _graveyard_informer;
164 }
165
166 //! @cond
167 // Structure for RAII-Style notification
168public:
169 /**
170 * This structure implements the Observer-mechanism RAII-Idiom.
171 * It triggers certain functions on creation and destruction so that
172 * Observer mechanisms can be linked to scope block.
173 */
174 class _Observable_protector {
175 public:
176 _Observable_protector(Observable *);
177 _Observable_protector(const _Observable_protector&);
178 ~_Observable_protector();
179 private:
180 Observable *protege;
181 };
182 //! @endcond
183};
184
185
186// extra macro is necessary to work with __LINE__
187#define PASTE(a,b) PASTE_HELPER(a,b)
188#define PASTE_HELPER(a,b) a ## b
189#define OBSERVE Observable::_Observable_protector PASTE(_scope_obs_protector_,__LINE__)(this)
190#define NOTIFY(channelno) do{Observable::enque_notification_internal(this,NotificationChannels[this]->getChannel(channelno));}while(0)
191#define LOCK_OBSERVABLE(observable) Observable::_Observable_protector PASTE(_scope_obs_protector_,__LINE__)(&(observable))
192
193#endif /* OBSERVABLE_HPP_ */
Note: See TracBrowser for help on using the repository browser.