Line data Source code
1 : //* This file is part of the MOOSE framework 2 : //* https://mooseframework.inl.gov 3 : //* 4 : //* All rights reserved, see COPYRIGHT for full restrictions 5 : //* https://github.com/idaholab/moose/blob/master/COPYRIGHT 6 : //* 7 : //* Licensed under LGPL 2.1, please see LICENSE for details 8 : //* https://www.gnu.org/licenses/lgpl-2.1.html 9 : 10 : #pragma once 11 : 12 : #include <memory> 13 : #include <vector> 14 : #include <utility> 15 : #include <iostream> 16 : 17 : template <class T> 18 : class UniqueStorage; 19 : template <typename T> 20 : void storeHelper(std::ostream & stream, UniqueStorage<T> &, void *); 21 : template <typename T> 22 : void loadHelper(std::istream & stream, UniqueStorage<T> &, void *); 23 : 24 : /** 25 : * Storage container that stores a vector of unique pointers of T, 26 : * but represents most of the public facing accessors (iterators, 27 : * operator[]) as a vector of T. 28 : * 29 : * That is, these accessors dereference the underlying storage. 30 : * More importantly, if data is not properly initialized using 31 : * setValue(), this dereferencing will either lead to an assertion 32 : * or a nullptr dereference. 33 : */ 34 : template <class T> 35 : class UniqueStorage 36 : { 37 : public: 38 1033884 : UniqueStorage() = default; 39 188262 : UniqueStorage(UniqueStorage &&) = default; 40 : UniqueStorage(const UniqueStorage & mp) = delete; 41 : UniqueStorage & operator=(const UniqueStorage &) = delete; 42 : 43 : /** 44 : * Iterator that adds an additional dereference to BaseIterator. 45 : */ 46 : template <class BaseIterator> 47 : struct DereferenceIterator : public BaseIterator 48 : { 49 670496 : DereferenceIterator(const BaseIterator & it) : BaseIterator(it) {} 50 : 51 : using value_type = typename BaseIterator::value_type::element_type; 52 : using pointer = value_type *; 53 : using reference = value_type &; 54 : 55 5041501 : reference operator*() const 56 : { 57 5041501 : auto & val = BaseIterator::operator*(); 58 : mooseAssert(val, "Null object"); 59 5041501 : return *val; 60 : } 61 : pointer operator->() const { return BaseIterator::operator*().get(); } 62 : reference operator[](size_t n) const 63 : { 64 : auto & val = BaseIterator::operator[](n); 65 : mooseAssert(val, "Null object"); 66 : return *val; 67 : } 68 : }; 69 : 70 : using values_type = typename std::vector<std::unique_ptr<T>>; 71 : using iterator = DereferenceIterator<typename values_type::iterator>; 72 : using const_iterator = DereferenceIterator<typename values_type::const_iterator>; 73 : 74 : /** 75 : * Begin and end iterators to the underlying data. 76 : * 77 : * Note that dereferencing these iterators may lead to an assertion 78 : * or the dereference of a nullptr whether or not the underlying data 79 : * is initialized. 80 : */ 81 : ///@{ 82 274142 : iterator begin() { return iterator(_values.begin()); } 83 274142 : iterator end() { return iterator(_values.end()); } 84 61106 : const_iterator begin() const { return const_iterator(_values.begin()); } 85 61106 : const_iterator end() const { return const_iterator(_values.end()); } 86 : ///@} 87 : 88 : /** 89 : * @returns A reference to the underlying data at index \p i. 90 : * 91 : * Note that the underlying data may not necessarily be initialized, 92 : * in which case this will throw an assertion or dereference a nullptr. 93 : * 94 : * You can check whether or not the underlying data is initialized 95 : * with hasValue(i). 96 : */ 97 : ///@{ 98 102183765 : const T & operator[](const std::size_t i) const 99 : { 100 : mooseAssert(hasValue(i), "Null object"); 101 102183765 : return *pointerValue(i); 102 : } 103 100717971 : T & operator[](const std::size_t i) { return const_cast<T &>(std::as_const(*this)[i]); } 104 : ///@} 105 : 106 : /** 107 : * @returns The size of the underlying storage. 108 : * 109 : * Note that this is not necessarily the size of _constructed_ objects, 110 : * as underlying objects could be uninitialized 111 : */ 112 59430197 : std::size_t size() const { return _values.size(); } 113 : /** 114 : * @returns Whether or not the underlying storage is empty. 115 : */ 116 11505054 : bool empty() const { return _values.empty(); } 117 : 118 : /** 119 : * @returns whether or not the underlying object at index \p is initialized 120 : */ 121 67335993 : bool hasValue(const std::size_t i) const { return pointerValue(i) != nullptr; } 122 : 123 : /** 124 : * @returns A pointer to the underlying data at index \p i 125 : * 126 : * The pointer will be nullptr if !hasValue(i), that is, if the 127 : * unique_ptr at index \p i is not initialized 128 : */ 129 : ///@{ 130 130689 : const T * queryValue(const std::size_t i) const { return pointerValue(i).get(); } 131 71274 : T * queryValue(const std::size_t i) 132 : { 133 71274 : return const_cast<T *>(std::as_const(*this).queryValue(i)); 134 : } 135 : ///@} 136 : 137 : protected: 138 : /** 139 : * Sets the underlying unique_ptr at index \p i to \p ptr. 140 : * 141 : * This can be used to construct objects in the storage, i.e., 142 : * setPointer(0, std::make_unique<T>(...)); 143 : */ 144 578479 : void setPointer(const std::size_t i, std::unique_ptr<T> && ptr) 145 : { 146 : mooseAssert(size() > i, "Invalid size"); 147 578479 : _values[i] = std::move(ptr); 148 578479 : } 149 : 150 : /** 151 : * Adds the given object in \p ptr to the storage. 152 : */ 153 2336391 : T & addPointer(std::unique_ptr<T> && ptr) 154 : { 155 : mooseAssert(ptr, "Null object"); 156 2336391 : return *_values.emplace_back(std::move(ptr)); 157 : } 158 : 159 : /** 160 : * Resizes the underlying vector. 161 : */ 162 383224 : void resize(const std::size_t size) { _values.resize(size); } 163 : 164 : /** 165 : * Clears the underlying vector. 166 : */ 167 : void clear() { _values.clear(); } 168 : 169 : private: 170 : /** 171 : * Returns a read-only reference to the underlying unique pointer 172 : * at index \p i. 173 : */ 174 169650453 : const std::unique_ptr<T> & pointerValue(const std::size_t i) const 175 : { 176 : mooseAssert(size() > i, "Invalid size"); 177 169650453 : return _values[i]; 178 : } 179 : /** 180 : * Returns a reference to the underlying unique pointer 181 : * at index \p i. 182 : */ 183 6 : std::unique_ptr<T> & pointerValue(const std::size_t i) 184 : { 185 6 : return const_cast<std::unique_ptr<T> &>(std::as_const(*this).pointerValue(i)); 186 : } 187 : 188 : friend void storeHelper<>(std::ostream & stream, UniqueStorage<T> &, void *); 189 : friend void loadHelper<>(std::istream & stream, UniqueStorage<T> &, void *); 190 : 191 : /// The underlying data 192 : values_type _values; 193 : };