Here's an example of a LazyObject. The base class provides the infrastructure, and performs manipulation of const-ness. I give a simple addition as an example. The test program demonstrates that the evaluation is performed only once. To show the advantages more fully, the addition object should, itself, be composed of Lazy<int> objects. --DaveWhipp
// DaveWhipp, 2-June-2000 #include <iostream> using namespace std; namespace infrastructure { template<class ValueType> class Lazy { public: virtual void evaluate() = 0; inline const ValueType& getValue() const { if (! _cacheIsValid) { const_cast<LazyObject*>(this)->evaluate(); _cacheIsValid = true; } return _cachedValue; } protected: inline Lazy() : _cacheIsValid(false) {} inline void setCachedValue(const ValueType& arg) { _cachedValue = arg; _cacheIsValid = true; } private: mutable ValueType _cachedValue; mutable bool _cacheIsValid; }; } // Simple class to test lazy evaluation // class Addition : public infrastructure::Lazy<int> { public: Addition(int a, int b) : Lazy(), _a(a), _b(b) {} void evaluate() { cout << "calculating: " << _a << " + " << _b << endl; setCachedValue(_a+_b); } private: int _a; int _b; }; // demonstrate that it works! // main() { const Addition a(4,6); cout << "Addition object exists; now show its value" << endl; cout << "1 result: " << a.getValue() << endl; cout << "2 result: " << a.getValue() << endl; cout << "3 result: " << a.getValue() << endl; }
Cool. I think you might want to make it so that evaluate() can just return the lazy value. It makes it more...natural, maybe? I can't really tell you why I think that that way's better... It only requires a small change, though.
public: virtual const ValueType& evaluate() = 0; inline const ValueType& getValue() const { if (! _cacheIsValid) { _cachedValue = const_cast<LazyObject*>(this)->evaluate(); _cacheIsValid = true; } return _cachedValue; } // Later: int Addition::evaluate() {cout << "Evaluating!"; return _a + _b}