source: src/CodePatterns/Observer/Observable.hpp@ 454bc54

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

Added new pattern ObservedValue, contrary to Cacheable.

  • essentially updates immediately and caches a value for off-line use, safe- guard with mutexes.
  • added unit test for ObservedValue.
  • factored threeNumbers stub out of CacheableUnitTest, also used the stub in ObservedValueUnitTest.
  • we may now signOn() to Notification with a priority.
  • Cacheable can also update now via notification channels not just for global updates.
  • Observable can now also instantiate the channels directly if given a list. This was necessary as Cacheable or ObservedValue are instantiated in the constructor, too, and if listening to channels, these must already be present.
  • FIX: Channels::addChannels() used NDEBUG wrong way round.
  • ObservedValue::get() returns reference.
  • Property mode set to 100644
File size: 6.4 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 //!> typedef for a vector of channels
48 typedef std::vector<size_t> channels_t;
49
50 Observable(std::string _name, const channels_t &_channels = channels_t());
51 virtual ~Observable();
52
53 /** This class is only used to distinguish from size_t in the overload.
54 *
55 * It encapsulates a const int (the priority level) and checks valid bounds
56 * in constructor.
57 *
58 */
59 class PriorityLevel {
60 public:
61 explicit PriorityLevel(const int i);
62 ~PriorityLevel();
63
64 const int level;
65 private:
66 static range<int> ValidRange;
67 };
68
69private:
70 /** Helper class to create a unique increasing list.
71 *
72 */
73 struct UniqueNumber
74 {
75 int operator()()
76 { return value++; }
77 int value;
78 };
79
80public:
81 static channels_t getChannelList(const size_t max);
82
83 /**
84 * Sign an Observer on to this Observable. The Observer will be notified
85 * whenever something inside the Observable changes. The Observer can
86 * assign itself a priority for the changes in the range of -20:+20.
87 * The Observer with lower priority will be called before the others,
88 * same as with Unix nice-levels. This can be used when an Object
89 * contains other objects that observe it (derived values), and these objects have
90 * to recalculate their states before the changes should be propageted to the
91 * UI. A default priority of 0 should be fine in most cases, since there is
92 * ussually no need to order the update sequence.
93 */
94 virtual void signOn(Observer *target, PriorityLevel priority = PriorityDefault) const;
95
96 /**
97 * Sign of a previously signed on Observer. After this no more
98 * updates will be recieved from that observer.
99 */
100 virtual void signOff(Observer *target) const;
101
102 /**
103 * Sign on for specialized notifications
104 */
105 virtual void signOn(
106 Observer *target,
107 size_t channelno,
108 PriorityLevel priority = PriorityDefault) const;
109
110 /**
111 * Stop receiving a specialized notification
112 */
113 virtual void signOff(Observer *target, size_t channelno) const;
114
115 /**
116 * Ask an Observer if it is currently in a blocked state, i.e. if
117 * Changes are in Progress, that are not yet published.
118 */
119 virtual bool isBlocked() const;
120
121 Notification_ptr getChannel(size_t no) const;
122
123 size_t getNumberOfObservers() const;
124
125protected:
126 virtual void update(Observable *publisher);
127 virtual void subjectKilled(Observable *publisher);
128
129 virtual void notifyAll();
130protected:
131// Observer mechanism is done from a static central place
132 /**
133 * Internal method.
134 * Do not call directly. Use OBSERVE macro instead
135 */
136 static void start_observer_internal(Observable *publisher);
137 /**
138 * Internal method.
139 * Do not call directly. Use OBSERVE macro instead
140 */
141 static void finish_observer_internal(Observable *publisher);
142
143 static void enque_notification_internal(Observable *publisher, Notification_ptr notification);
144
145 typedef std::map<Observable*, Channels *> ChannelMap;
146 static ChannelMap NotificationChannels;
147
148 static PriorityLevel PriorityDefault;
149
150protected:
151 typedef std::multimap<int,Observer*> callees_t;
152 typedef std::set<Notification*> notificationSet;
153 static std::map<Observable*, int> depth;
154 static std::map<Observable*,callees_t> callTable;
155 static std::map<Observable*,notificationSet> notifications;
156 static std::set<Observable*> busyObservables;
157
158 static boost::recursive_mutex ObservablesMapLock; //!< a lock for the pointer of the instance
159
160private:
161 friend class Zombie;
162 friend class Graveyard;
163
164 typedef boost::function<void (const Observable*)> graveyard_informer_t;
165
166 /** Bound function to call when Observer are signing off (needs to be a ptr
167 * as we must be able to rebound it.
168 *
169 * \warning Do not delete this pointer, the instance is either a static one
170 * or handled someplace else (e.g. in the Graveyard).
171 */
172 graveyard_informer_t * graveyard_informer;
173
174 //!> default informer that does nothing
175 static graveyard_informer_t noop_informer;
176
177 /** Sets the bound function for over-time life-time management.
178 *
179 * \param _graveyard ptr Graveyard to inform of leaving Observers
180 */
181 void setGraveyardInformer(graveyard_informer_t * _graveyard_informer)
182 {
183 graveyard_informer = _graveyard_informer;
184 }
185
186 //! @cond
187 // Structure for RAII-Style notification
188public:
189 /**
190 * This structure implements the Observer-mechanism RAII-Idiom.
191 * It triggers certain functions on creation and destruction so that
192 * Observer mechanisms can be linked to scope block.
193 */
194 class _Observable_protector {
195 public:
196 _Observable_protector(Observable *);
197 _Observable_protector(const _Observable_protector&);
198 ~_Observable_protector();
199 private:
200 Observable *protege;
201 };
202 //! @endcond
203};
204
205
206// extra macro is necessary to work with __LINE__
207#define PASTE(a,b) PASTE_HELPER(a,b)
208#define PASTE_HELPER(a,b) a ## b
209#define OBSERVE Observable::_Observable_protector PASTE(_scope_obs_protector_,__LINE__)(this)
210#define NOTIFY(channelno) do{Observable::enque_notification_internal(this,NotificationChannels[this]->getChannel(channelno));}while(0)
211#define LOCK_OBSERVABLE(observable) Observable::_Observable_protector PASTE(_scope_obs_protector_,__LINE__)(&(observable))
212
213#endif /* OBSERVABLE_HPP_ */
Note: See TracBrowser for help on using the repository browser.