source: src/CodePatterns/Cacheable.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: 8.8 KB
Line 
1/*
2 * Cacheable.hpp
3 *
4 * Created on: Feb 2, 2010
5 * Author: crueger
6 */
7
8#ifndef CACHEABLE_HPP_
9#define CACHEABLE_HPP_
10
11// include config.h
12#ifdef HAVE_CONFIG_H
13#include <config.h>
14#endif
15
16#include "Observer/Observable.hpp"
17#include "Observer/Observer.hpp"
18#include <boost/function.hpp>
19#include <boost/shared_ptr.hpp>
20
21#include "CodePatterns/Assert.hpp"
22
23#include "CodePatterns/Observer/Notification.hpp"
24
25#ifndef NO_CACHING
26
27 template<typename T>
28 class Cacheable : public Observer
29 {
30 // we define the states of the cacheable so we can do very fast state-checks
31 class State{
32 public:
33 State(Cacheable *_owner) :
34 busy(false),
35 owner(_owner)
36 {}
37 virtual T& getValue()=0;
38 virtual void invalidate()=0;
39 virtual bool isValid()=0;
40 virtual void enter()=0;
41 bool isBusy(){
42 return busy;
43 }
44 virtual std::string getName()=0;
45 protected:
46 bool busy;
47 Cacheable *owner;
48 };
49
50 class InvalidState : public State{
51 public:
52 InvalidState(Cacheable *_owner):
53 State(_owner)
54 {}
55
56 virtual T& getValue(){
57 // set the state to valid
58 State::owner->switchState(State::owner->validState);
59 // get the value from the now valid state
60 return State::owner->state->getValue();
61 }
62
63 virtual void invalidate(){
64 // nothing to do on this message
65 }
66
67 virtual bool isValid(){
68 return false;
69 }
70
71 virtual void enter(){
72 // nothing to do when entering this
73 }
74
75 virtual std::string getName(){
76 return "invalid";
77 }
78 };
79
80 class ValidState : public State{
81 public:
82 ValidState(Cacheable *_owner) :
83 State(_owner)
84 {}
85
86 virtual T& getValue(){
87 return content;
88 }
89
90 virtual void invalidate(){
91 State::owner->switchState(State::owner->invalidState);
92 }
93
94 virtual bool isValid(){
95 return true;
96 }
97
98 virtual void enter(){
99 State::busy= true;
100 // as soon as we enter the valid state we recalculate
101 content = State::owner->recalcMethod();
102 State::busy = false;
103 }
104
105 virtual std::string getName(){
106 return "valid";
107 }
108 private:
109 T content;
110 };
111
112 class DestroyedState : public State {
113 public:
114 DestroyedState(Cacheable *_owner) :
115 State(_owner)
116 {}
117
118 virtual T& getValue(){
119 ASSERT(0,"Cannot get a value from a Cacheable after it's Observable has died");
120 // we have to return a grossly invalid reference, because no value can be produced anymore
121 return *(static_cast<T*>(0));
122 }
123
124 virtual void invalidate(){
125 ASSERT(0,"Cannot invalidate a Cacheable after it's Observable has died");
126 }
127
128 virtual bool isValid(){
129 ASSERT(0,"Cannot check validity of a Cacheable after it's Observable has died");
130 return false;
131 }
132
133 virtual void enter(){
134 // nothing to do when entering this state
135 }
136
137 virtual std::string getName(){
138 return "destroyed";
139 }
140 };
141
142
143 typedef boost::shared_ptr<State> state_ptr;
144
145 public:
146 Cacheable(
147 Observable *_owner,
148 const boost::function<T()> _recalcMethod,
149 const std::string &name,
150 const Observable::channels_t &_channels = Observable::channels_t());
151 virtual ~Cacheable();
152
153 const bool isValid() const;
154 const T operator*() const;
155
156 // methods implemented for base-class Observer
157 void update(Observable *subject);
158 void recieveNotification(Observable *publisher, Notification_ptr notification);
159 void subjectKilled(Observable *subject);
160 private:
161 void switchState(state_ptr newState);
162
163 mutable state_ptr state;
164 // pre-defined state so we don't have to construct to much
165 state_ptr invalidState;
166 state_ptr validState;
167 // destroyed state is not predefined, because we rarely enter that state and never leave
168
169 const Observable * owner;
170 const boost::function<T()> recalcMethod;
171
172 //!> contains list of possible channels to enlist, if empty we signOn globally
173 const Observable::channels_t channels;
174
175 // de-activated copy constructor
176 Cacheable(const Cacheable&);
177 };
178
179
180 template<typename T>
181 Cacheable<T>::Cacheable(
182 Observable *_owner,
183 const boost::function<T()> _recalcMethod,
184 const std::string &_name,
185 const Observable::channels_t &_channels) :
186 Observer(_name + "(Cached)"),
187 owner(_owner),
188 recalcMethod(_recalcMethod),
189 channels(_channels)
190 {
191 // create all states needed for this object
192 invalidState = state_ptr(new InvalidState(this));
193 validState = state_ptr(new ValidState(this));
194 state = invalidState;
195 // we sign on with the best(=lowest) priority, so cached values are recalculated before
196 // anybody else might ask for updated values
197 if (owner != NULL) {
198 if (channels.empty()) {
199 owner->signOn(this,Observable::PriorityLevel(int(-20)));
200 } else {
201 for (Observable::channels_t::const_iterator iter = channels.begin();
202 iter != channels.end(); ++iter)
203 owner->signOn(this,*iter,Observable::PriorityLevel(int(-20)));
204 }
205 }
206 }
207
208 // de-activated copy constructor
209 template<typename T>
210 Cacheable<T>::Cacheable(const Cacheable&){
211 ASSERT(0,"Cacheables should never be copied");
212 }
213
214 template<typename T>
215 const T Cacheable<T>::operator*() const{
216 // we can only use the cacheable when the owner is not changing at the moment
217 if ((owner == NULL) || (!owner->isBlocked())) {
218 return state->getValue();
219 }
220 else{
221 return recalcMethod();
222 }
223 }
224
225 template<typename T>
226 Cacheable<T>::~Cacheable()
227 {
228 if (owner != NULL) {
229 if (channels.empty()) {
230 owner->signOff(this);
231 } else {
232 for (Observable::channels_t::const_iterator iter = channels.begin();
233 iter != channels.end(); ++iter)
234 owner->signOff(this,*iter);
235 }
236 }
237 }
238
239 template<typename T>
240 const bool Cacheable<T>::isValid() const{
241 return state->isValid();
242 }
243
244 template<typename T>
245 void Cacheable<T>::update(Observable *subject) {
246 ASSERT( channels.empty(),
247 "Cacheable<T>::update() - we are listening only to global updates.");
248 state->invalidate();
249 }
250
251 template<typename T>
252 void Cacheable<T>::recieveNotification(Observable *publisher, Notification_ptr notification) {
253 if (publisher == owner) {
254 ASSERT( !channels.empty(),
255 "Cacheable<T>::update() - we are not listening to global updates.");
256 const Observable::channels_t::const_iterator iter = std::find(
257 channels.begin(), channels.end(), notification->getChannelNo());
258 if (iter != channels.end())
259 state->invalidate();
260 }
261 }
262
263 template<typename T>
264 void Cacheable<T>::subjectKilled(Observable *subject) {
265 state_ptr destroyed = state_ptr(new DestroyedState(this));
266 switchState(destroyed);
267 }
268
269 template<typename T>
270 void Cacheable<T>::switchState(state_ptr newState){
271 ASSERT(!state->isBusy(),"LOOP DETECTED: Cacheable state switched while recalculating.\nDid the recalculation trigger the Observable?");
272#ifdef LOG_OBSERVER
273 observerLog().addMessage() << "## Cacheable " << observerLog().getName(this) << " changed state (" << state->getName()
274 << "->" << newState->getName() << ")";
275#endif
276 state = newState;
277 state->enter();
278 }
279
280#else
281 template<typename T>
282 class Cacheable : public Observer
283 {
284 public:
285 Cacheable(Observable *_owner, boost::function<T()> _recalcMethod,std::string name);
286 virtual ~Cacheable();
287
288 const bool isValid() const;
289 const T operator*() const;
290
291 // methods implemented for base-class Observer
292 void update(Observable *subject);
293 void recieveNotification(Observable *publisher, Notification_ptr notification);
294 void subjectKilled(Observable *subject);
295 private:
296
297 boost::function<T()> recalcMethod;
298 };
299
300 template<typename T>
301 Cacheable<T>::Cacheable(Observable *_owner, boost::function<T()> _recalcMethod, std::string name) :
302 Observer(name),
303 recalcMethod(_recalcMethod)
304 {}
305
306 template<typename T>
307 const T Cacheable<T>::operator*() const{
308 return recalcMethod();
309 }
310
311 template<typename T>
312 Cacheable<T>::~Cacheable()
313 {}
314
315 template<typename T>
316 const bool Cacheable<T>::isValid() const{
317 return true;
318 }
319
320 template<typename T>
321 void Cacheable<T>::update(Observable *subject) {
322 ASSERT(0, "Cacheable::update should never be called when caching is disabled");
323 }
324
325 template<typename T>
326 void Cacheable<T>::recieveNotification(Observable *publisher, Notification_ptr notification) {
327 ASSERT(0, "Cacheable::recieveNotification should never be called when caching is disabled");
328 }
329
330 template<typename T>
331 void Cacheable<T>::subjectKilled(Observable *subject){
332 ASSERT(0, "Cacheable::subjectKilled should never be called when caching is disabled");
333 }
334#endif
335
336#endif /* CACHEABLE_HPP_ */
Note: See TracBrowser for help on using the repository browser.