source: src/Patterns/Cacheable.hpp@ 70672e3

Last change on this file since 70672e3 was 70672e3, checked in by Frederik Heber <heber@…>, 15 years ago

Added config.h to all header files.

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