/* * Cacheable.hpp * * Created on: Feb 2, 2010 * Author: crueger */ #ifndef CACHEABLE_HPP_ #define CACHEABLE_HPP_ #include "Patterns/Observer.hpp" #include #include #include #include "Helpers/Assert.hpp" #ifndef NO_CACHING template class Cacheable : public Observer { // we define the states of the cacheable so we can do very fast state-checks class State{ public: State(Cacheable *_owner) : busy(false), owner(_owner) {} virtual T getValue()=0; virtual void invalidate()=0; virtual bool isValid()=0; virtual void enter()=0; bool isBusy(){ return busy; } protected: bool busy; Cacheable *owner; }; class InvalidState : public State{ public: InvalidState(Cacheable *_owner): State(_owner) {} virtual T getValue(){ // set the state to valid State::owner->switchState(State::owner->validState); // get the value from the now valid state return State::owner->state->getValue(); } virtual void invalidate(){ // nothing to do on this message } virtual bool isValid(){ return false; } virtual void enter(){ // nothing to do when entering this } }; class ValidState : public State{ public: ValidState(Cacheable *_owner) : State(_owner) {} virtual T getValue(){ return content; } virtual void invalidate(){ State::owner->switchState(State::owner->invalidState); } virtual bool isValid(){ return true; } virtual void enter(){ State::busy= true; // as soon as we enter the valid state we recalculate content = State::owner->recalcMethod(); State::busy = false; } private: T content; }; class DestroyedState : public State { public: DestroyedState(Cacheable *_owner) : State(_owner) {} virtual T getValue(){ ASSERT(0,"Cannot get a value from a Cacheable after it's Observable has died"); // we have to return a grossly invalid reference, because no value can be produced anymore return *(static_cast(0)); } virtual void invalidate(){ ASSERT(0,"Cannot invalidate a Cacheable after it's Observable has died"); } virtual bool isValid(){ ASSERT(0,"Cannot check validity of a Cacheable after it's Observable has died"); return false; } virtual void enter(){ // nothing to do when entering this state } }; typedef boost::shared_ptr state_ptr; public: Cacheable(Observable *_owner, boost::function _recalcMethod); virtual ~Cacheable(); const bool isValid() const; const T operator*() const; // methods implemented for base-class Observer void update(Observable *subject); void subjectKilled(Observable *subject); private: void switchState(state_ptr newState); mutable state_ptr state; // pre-defined state so we don't have to construct to much state_ptr invalidState; state_ptr validState; // destroyed state is not predefined, because we rarely enter that state and never leave Observable *owner; boost::function recalcMethod; // de-activated copy constructor Cacheable(const Cacheable&); }; template Cacheable::Cacheable(Observable *_owner, boost::function _recalcMethod) : owner(_owner), recalcMethod(_recalcMethod) { // create all states needed for this object invalidState = state_ptr(new InvalidState(this)); validState = state_ptr(new ValidState(this)); state = invalidState; // we sign on with the best(=lowest) priority, so cached values are recalculated before // anybody else might ask for updated values owner->signOn(this,-20); } // de-activated copy constructor template Cacheable::Cacheable(const Cacheable&){ ASSERT(0,"Cacheables should never be copied"); } template const T Cacheable::operator*() const{ // we can only use the cacheable when the owner is not changing at the moment if(!owner->isBlocked()){ return state->getValue(); } else{ return recalcMethod(); } } template Cacheable::~Cacheable() { owner->signOff(this); } template const bool Cacheable::isValid() const{ return state->isValid(); } template void Cacheable::update(Observable *subject) { state->invalidate(); } template void Cacheable::subjectKilled(Observable *subject) { state_ptr destroyed = state_ptr(new DestroyedState(this)); switchState(destroyed); } template void Cacheable::switchState(state_ptr newState){ ASSERT(!state->isBusy(),"LOOP DETECTED: Cacheable state switched while recalculating.\nDid the recalculation trigger the Observable?"); state = newState; state->enter(); } #else template class Cacheable : public Observer { public: Cacheable(Observable *_owner, boost::function _recalcMethod); virtual ~Cacheable(); const bool isValid() const; const T operator*() const; // methods implemented for base-class Observer void update(Observable *subject); void subjectKilled(Observable *subject); private: boost::function recalcMethod; }; template Cacheable::Cacheable(Observable *_owner, boost::function _recalcMethod) : recalcMethod(_recalcMethod) {} template const T Cacheable::operator*() const{ return recalcMethod(); } template Cacheable::~Cacheable() {} template const bool Cacheable::isValid() const{ return true; } template void Cacheable::update(Observable *subject) { ASSERT(0, "Cacheable::update should never be called when caching is disabled"); } template void Cacheable::subjectKilled(Observable *subject){ ASSERT(0, "Cacheable::subjectKilled should never be called when caching is disabled"); } #endif #endif /* CACHEABLE_HPP_ */