summaryrefslogtreecommitdiffstats
path: root/src/LazyArray.h
diff options
context:
space:
mode:
Diffstat (limited to 'src/LazyArray.h')
-rw-r--r--src/LazyArray.h134
1 files changed, 134 insertions, 0 deletions
diff --git a/src/LazyArray.h b/src/LazyArray.h
new file mode 100644
index 000000000..7015d6c47
--- /dev/null
+++ b/src/LazyArray.h
@@ -0,0 +1,134 @@
+
+#pragma once
+
+/** A dynamic array that defers allocation to the first modifying access.
+Const references from the array before allocation will all be to the same default constructed value.
+It is therefore important that default constructed values are indistinguishable from each other. */
+template <typename T>
+class cLazyArray
+{
+ static_assert((!std::is_reference<T>::value && !std::is_array<T>::value),
+ "cLazyArray<T>: T must be a value type");
+ static_assert(std::is_default_constructible<T>::value,
+ "cLazyArray<T>: T must be default constructible");
+public:
+ using value_type = T;
+ using pointer = T *;
+ using const_pointer = const T *;
+ using reference = T &;
+ using const_reference = const T &;
+ using size_type = int;
+ using iterator = pointer;
+ using const_iterator = const_pointer;
+
+ cLazyArray(size_type a_Size) NOEXCEPT:
+ m_Size{ a_Size }
+ {
+ ASSERT(a_Size > 0);
+ }
+
+ cLazyArray(const cLazyArray & a_Other):
+ m_Size{ a_Other.m_Size }
+ {
+ if (a_Other.IsStorageAllocated())
+ {
+ // Note that begin() will allocate the array to copy into
+ std::copy(a_Other.begin(), a_Other.end(), begin());
+ }
+ }
+
+ cLazyArray(cLazyArray && a_Other) NOEXCEPT:
+ m_Array{ std::move(a_Other.m_Array) },
+ m_Size{ a_Other.m_Size }
+ {
+ }
+
+ cLazyArray & operator = (const cLazyArray & a_Other)
+ {
+ cLazyArray(a_Other).swap(*this);
+ return *this;
+ }
+
+ cLazyArray & operator = (cLazyArray && a_Other) NOEXCEPT
+ {
+ m_Array = std::move(a_Other.m_Array);
+ m_Size = a_Other.m_Size;
+ return *this;
+ }
+
+ T & operator [] (size_type a_Idx)
+ {
+ ASSERT((0 <= a_Idx) && (a_Idx < m_Size));
+ return data()[a_Idx];
+ }
+
+ const T & operator [] (size_type a_Idx) const
+ {
+ return GetAt(a_Idx);
+ }
+
+ // STL style interface
+
+ const_iterator cbegin() const { return data(); }
+ iterator begin() { return data(); }
+ const_iterator begin() const { return cbegin(); }
+
+ const_iterator cend() const { return data() + m_Size; }
+ iterator end() { return data() + m_Size; }
+ const_iterator end() const { return cend(); }
+
+ size_type size() const NOEXCEPT { return m_Size; }
+
+ const T * data() const
+ {
+ if (m_Array == nullptr)
+ {
+ m_Array.reset(new T[m_Size]);
+ }
+ return m_Array.get();
+ }
+
+ T * data()
+ {
+ static_assert(!std::is_const<typename decltype(m_Array)::element_type>::value, "");
+ const cLazyArray * const_this = this;
+ return const_cast<T *>(const_this->data());
+ }
+
+ void swap(cLazyArray & a_Other) NOEXCEPT
+ {
+ std::swap(m_Array, a_Other.m_Array);
+ std::swap(m_Size, a_Other.m_Size);
+ }
+
+ friend void swap(cLazyArray & a_Lhs, cLazyArray & a_Rhs) NOEXCEPT
+ {
+ a_Lhs.swap(a_Rhs);
+ }
+
+ // Extra functions to help avoid allocation
+
+ /** A const view of an element of the array. Never causes the array to allocate. */
+ const T & GetAt(size_type a_Idx) const
+ {
+ ASSERT((0 <= a_Idx) && (a_Idx < m_Size));
+ if (IsStorageAllocated())
+ {
+ return data()[a_Idx];
+ }
+ else
+ {
+ static const T DefaultValue;
+ return DefaultValue;
+ }
+ }
+
+ /** Returns true if the array has already been allocated. */
+ bool IsStorageAllocated() const NOEXCEPT { return (m_Array != nullptr); }
+
+private:
+ // Mutable so const data() can allocate the array
+ mutable std::unique_ptr<T[]> m_Array;
+ size_type m_Size;
+};
+