source: src/CodePatterns/Cacheable.hpp@ 163eec

Last change on this file since 163eec was 37d941, checked in by Frederik Heber <heber@…>, 12 years ago

Cacheable may be used without an active Observable.

  • this allows to use Cacheable pattern as a simple lazy evaluation. If the actual calculation is expensive but the object never changes, this can be very beneficial.
  • Property mode set to 100644
File size: 7.0 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#ifndef NO_CACHING
24
25 template<typename T>
26 class Cacheable : public Observer
27 {
28 // we define the states of the cacheable so we can do very fast state-checks
29 class State{
30 public:
31 State(Cacheable *_owner) :
32 busy(false),
33 owner(_owner)
34 {}
35 virtual T& getValue()=0;
36 virtual void invalidate()=0;
37 virtual bool isValid()=0;
38 virtual void enter()=0;
39 bool isBusy(){
40 return busy;
41 }
42 virtual std::string getName()=0;
43 protected:
44 bool busy;
45 Cacheable *owner;
46 };
47
48 class InvalidState : public State{
49 public:
50 InvalidState(Cacheable *_owner):
51 State(_owner)
52 {}
53
54 virtual T& getValue(){
55 // set the state to valid
56 State::owner->switchState(State::owner->validState);
57 // get the value from the now valid state
58 return State::owner->state->getValue();
59 }
60
61 virtual void invalidate(){
62 // nothing to do on this message
63 }
64
65 virtual bool isValid(){
66 return false;
67 }
68
69 virtual void enter(){
70 // nothing to do when entering this
71 }
72
73 virtual std::string getName(){
74 return "invalid";
75 }
76 };
77
78 class ValidState : public State{
79 public:
80 ValidState(Cacheable *_owner) :
81 State(_owner)
82 {}
83
84 virtual T& getValue(){
85 return content;
86 }
87
88 virtual void invalidate(){
89 State::owner->switchState(State::owner->invalidState);
90 }
91
92 virtual bool isValid(){
93 return true;
94 }
95
96 virtual void enter(){
97 State::busy= true;
98 // as soon as we enter the valid state we recalculate
99 content = State::owner->recalcMethod();
100 State::busy = false;
101 }
102
103 virtual std::string getName(){
104 return "valid";
105 }
106 private:
107 T content;
108 };
109
110 class DestroyedState : public State {
111 public:
112 DestroyedState(Cacheable *_owner) :
113 State(_owner)
114 {}
115
116 virtual T& getValue(){
117 ASSERT(0,"Cannot get a value from a Cacheable after it's Observable has died");
118 // we have to return a grossly invalid reference, because no value can be produced anymore
119 return *(static_cast<T*>(0));
120 }
121
122 virtual void invalidate(){
123 ASSERT(0,"Cannot invalidate a Cacheable after it's Observable has died");
124 }
125
126 virtual bool isValid(){
127 ASSERT(0,"Cannot check validity of a Cacheable after it's Observable has died");
128 return false;
129 }
130
131 virtual void enter(){
132 // nothing to do when entering this state
133 }
134
135 virtual std::string getName(){
136 return "destroyed";
137 }
138 };
139
140
141 typedef boost::shared_ptr<State> state_ptr;
142
143 public:
144 Cacheable(Observable *_owner, boost::function<T()> _recalcMethod, std::string name);
145 virtual ~Cacheable();
146
147 const bool isValid() const;
148 const T operator*() const;
149
150 // methods implemented for base-class Observer
151 void update(Observable *subject);
152 void subjectKilled(Observable *subject);
153 private:
154 void switchState(state_ptr newState);
155
156 mutable state_ptr state;
157 // pre-defined state so we don't have to construct to much
158 state_ptr invalidState;
159 state_ptr validState;
160 // destroyed state is not predefined, because we rarely enter that state and never leave
161
162 Observable *owner;
163 boost::function<T()> recalcMethod;
164
165 // de-activated copy constructor
166 Cacheable(const Cacheable&);
167 };
168
169
170 template<typename T>
171 Cacheable<T>::Cacheable(Observable *_owner, boost::function<T()> _recalcMethod, std::string name) :
172 Observer(name + "(Cached)"),
173 owner(_owner),
174 recalcMethod(_recalcMethod)
175 {
176 // create all states needed for this object
177 invalidState = state_ptr(new InvalidState(this));
178 validState = state_ptr(new ValidState(this));
179 state = invalidState;
180 // we sign on with the best(=lowest) priority, so cached values are recalculated before
181 // anybody else might ask for updated values
182 if (owner != NULL)
183 owner->signOn(this,Observable::PriorityLevel(int(-20)));
184 }
185
186 // de-activated copy constructor
187 template<typename T>
188 Cacheable<T>::Cacheable(const Cacheable&){
189 ASSERT(0,"Cacheables should never be copied");
190 }
191
192 template<typename T>
193 const T Cacheable<T>::operator*() const{
194 // we can only use the cacheable when the owner is not changing at the moment
195 if ((owner == NULL) || (!owner->isBlocked())) {
196 return state->getValue();
197 }
198 else{
199 return recalcMethod();
200 }
201 }
202
203 template<typename T>
204 Cacheable<T>::~Cacheable()
205 {
206 if (owner != NULL)
207 owner->signOff(this);
208 }
209
210 template<typename T>
211 const bool Cacheable<T>::isValid() const{
212 return state->isValid();
213 }
214
215 template<typename T>
216 void Cacheable<T>::update(Observable *subject) {
217 state->invalidate();
218 }
219
220 template<typename T>
221 void Cacheable<T>::subjectKilled(Observable *subject) {
222 state_ptr destroyed = state_ptr(new DestroyedState(this));
223 switchState(destroyed);
224 }
225
226 template<typename T>
227 void Cacheable<T>::switchState(state_ptr newState){
228 ASSERT(!state->isBusy(),"LOOP DETECTED: Cacheable state switched while recalculating.\nDid the recalculation trigger the Observable?");
229#ifdef LOG_OBSERVER
230 observerLog().addMessage() << "## Cacheable " << observerLog().getName(this) << " changed state (" << state->getName()
231 << "->" << newState->getName() << ")";
232#endif
233 state = newState;
234 state->enter();
235 }
236
237#else
238 template<typename T>
239 class Cacheable : public Observer
240 {
241 public:
242 Cacheable(Observable *_owner, boost::function<T()> _recalcMethod,std::string name);
243 virtual ~Cacheable();
244
245 const bool isValid() const;
246 const T operator*() const;
247
248 // methods implemented for base-class Observer
249 void update(Observable *subject);
250 void subjectKilled(Observable *subject);
251 private:
252
253 boost::function<T()> recalcMethod;
254 };
255
256 template<typename T>
257 Cacheable<T>::Cacheable(Observable *_owner, boost::function<T()> _recalcMethod, std::string name) :
258 Observer(name),
259 recalcMethod(_recalcMethod)
260 {}
261
262 template<typename T>
263 const T Cacheable<T>::operator*() const{
264 return recalcMethod();
265 }
266
267 template<typename T>
268 Cacheable<T>::~Cacheable()
269 {}
270
271 template<typename T>
272 const bool Cacheable<T>::isValid() const{
273 return true;
274 }
275
276 template<typename T>
277 void Cacheable<T>::update(Observable *subject) {
278 ASSERT(0, "Cacheable::update should never be called when caching is disabled");
279 }
280
281 template<typename T>
282 void Cacheable<T>::subjectKilled(Observable *subject){
283 ASSERT(0, "Cacheable::subjectKilled should never be called when caching is disabled");
284 }
285#endif
286
287#endif /* CACHEABLE_HPP_ */
Note: See TracBrowser for help on using the repository browser.