/***************************************************************************
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., OptionalVal) to deal with this.