/*************************************************************************** Lazy<T>: template class for lazy initialization of objects whose values do not change after initialization. In a multi-threaded environment, this makes use of "double checked locking" for an efficient, thread-safe solution. Usage: Lazy<T> obj; // declaration of the lazy object ... do { Lazy<T>::Builder builder(obj); if (!builder()) break; // if we are not building, the break out UniquePtr<T> p; // create a pointer ... builder.move(p); // move p into the object to complete the initialization // We can then complete the initialization process. } while(0); // When this scope closes, the object is fully initialized. // subsequent attempts to build the object will yield // !builder.built() T objCopy = *obj; // *obj returns a read-only reference // one can also use -> operator It is important to follow this recipe carefully. In particular, the builder must be enclosed in a scope, as it's destructor plays a crucial role in finalizing the initialization. NOTE: if p is null in builder.move(p), the object is still considered built. ****************************************************************************/ template<class T, class P=DefaultDeleterPolicy> class Lazy { public: Lazy(); Lazy(const Lazy&); Lazy& operator=(const Lazy&); // deep copies using T's copy constructor // EXCEPTIONS: may throw (but provides strong ES guarantee) const T& operator*() const; // pointer access const T* operator->() const; const T* get() const; operator fake_null_type() const; // allows test against 0 ~Lazy(); kill(); // destroy and reset bool built() const; // test if already built class Builder { Builder(const Lazy&); ~Builder() bool operator()() const; // test if we are building void move(UniquePtr<T,P>&); // EXCEPTIONS: may throw an exception if the move is not allowed // (i.e., not building or already moved). // Provides strong ES guarantee. }; }; // EXCEPTIONS: except where noted, no exceptions are thrown // NOTE: For more on double-checked locking, see // http://preshing.com/20130930/double-checked-locking-is-fixed-in-cpp11/ // NOTE: when compiled with the NTL_THREADS option, the Lazy // class may contain data members from the standard library // that may not satisfy the requirements of the Vec class // (i.e., relocatability). One can wrap it in a pointer // class (e.g., CopiedPtr) to deal with this. // NOTE: The optional parameter P is used as in the specification // of the UniquePtr class. The default should work fine in // most cases. It was introduced mainly to allow for the PIMPL // paradigm.