/*************************************************************************** LazyTable<T,MAX>: 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: LazyTable<T,MAX> tab; // declaration of the lazy table, // with max size == MAX ... do { LazyTable<T,MAX>::Builder builder(tab, n); // request length n long amt = builder.amt(); if (!amt) break; ... initialize elements i = n-amt..n-1 using builder.move(p), where p is a UnqiuePtr<T> note that each move application appends one element } while(0); // When this scope closes, // the table is fully initialized to length n const T* val = table[i]; // read-only access to table elements 0..n-1 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. ****************************************************************************/ template<class T, long MAX> class LazyTable { public: LazyTable(); ~LazyTable(); const T * const operator[] (long i) const; // element access -- currently no range checking long length() const; // current table length class Builder { Builder(const LazyTable&, long request); // EXCEPTIONS: may throw an exception if request is out of range // or if alocation of table fails ~Builder() long amt() const; void move(UniquePtr<T>& p); // EXCEPTIONS: throws exception of move is not allowed. // Provides strong ES guarantee. }; private: LazyTable(const LazyTable&); // disabled LazyTable& operator=(const LazyTable&); }; // 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 LazyTable // 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.