1 // Copyright 2024 The Pigweed Authors 2 // 3 // Licensed under the Apache License, Version 2.0 (the "License"); you may not 4 // use this file except in compliance with the License. You may obtain a copy of 5 // the License at 6 // 7 // https://www.apache.org/licenses/LICENSE-2.0 8 // 9 // Unless required by applicable law or agreed to in writing, software 10 // distributed under the License is distributed on an "AS IS" BASIS, WITHOUT 11 // WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the 12 // License for the specific language governing permissions and limitations under 13 // the License. 14 #pragma once 15 16 #include <cstddef> 17 #include <memory> 18 #include <utility> 19 20 #include "pw_allocator/capability.h" 21 #include "pw_preprocessor/compiler.h" 22 23 namespace pw { 24 25 namespace uniqueptr::internal { 26 struct Empty {}; 27 } // namespace uniqueptr::internal 28 29 // Forward declaration. 30 class Deallocator; 31 32 namespace allocator::internal { 33 34 /// This class simply provides type-erased static methods to check capabilities 35 /// and deallocate memory in a unique pointer. This allows ``UniquePtr<T>`` to 36 /// be declared without a complete declaration of ``Deallocator``, breaking the 37 /// dependency cycle between ``UniquePtr<T>` and ``Allocator::MakeUnique<T>()``. 38 class BaseUniquePtr { 39 protected: 40 using Capability = ::pw::allocator::Capability; 41 42 static bool HasCapability(Deallocator* deallocator, Capability capability); 43 static void Deallocate(Deallocator* deallocator, void* ptr); 44 }; 45 46 } // namespace allocator::internal 47 48 /// An RAII pointer to a value of type ``T`` stored in memory provided by a 49 /// ``Deallocator``. 50 /// 51 /// This is analogous to ``std::unique_ptr``, but includes a few differences 52 /// in order to support ``Deallocator`` and encourage safe usage. Most 53 /// notably, ``UniquePtr<T>`` cannot be constructed from a ``T*``. 54 template <typename T> 55 class UniquePtr : public allocator::internal::BaseUniquePtr { 56 public: 57 using UnderlyingType = 58 std::conditional_t<std::is_array_v<T>, 59 typename std::remove_extent<T>::type, 60 T>; 61 using Base = ::pw::allocator::internal::BaseUniquePtr; 62 63 /// Creates an empty (``nullptr``) instance. 64 /// 65 /// NOTE: Instances of this type are most commonly constructed using 66 /// ``Deallocator::MakeUnique``. UniquePtr()67 constexpr UniquePtr() : value_(nullptr), deallocator_(nullptr) {} 68 69 /// Creates an empty (``nullptr``) instance. 70 /// 71 /// NOTE: Instances of this type are most commonly constructed using 72 /// ``Deallocator::MakeUnique``. UniquePtr(std::nullptr_t)73 constexpr UniquePtr(std::nullptr_t) : UniquePtr() {} 74 75 /// Move-constructs a ``UniquePtr<T>`` from a ``UniquePtr<U>``. 76 /// 77 /// This allows not only pure move construction where ``T == U``, but also 78 /// converting construction where ``T`` is a base class of ``U``, like 79 /// ``UniquePtr<Base> base(deallocator.MakeUnique<Child>());``. 80 template <typename U> UniquePtr(UniquePtr<U> && other)81 UniquePtr(UniquePtr<U>&& other) noexcept 82 : value_(other.value_), 83 deallocator_(other.deallocator_), 84 size_(other.size_) { 85 static_assert( 86 std::is_assignable_v<UnderlyingType*&, 87 typename UniquePtr<U>::UnderlyingType*>, 88 "Attempted to construct a UniquePtr<T> from a UniquePtr<U> where " 89 "U* is not assignable to T*."); 90 other.Release(); 91 } 92 93 // Move-only. These are needed since the templated move-contructor and 94 // move-assignment operator do not exactly match the signature of the default 95 // move-contructor and move-assignment operator, and thus do not implicitly 96 // delete the copy-contructor and copy-assignment operator. 97 UniquePtr(const UniquePtr&) = delete; 98 UniquePtr& operator=(const UniquePtr&) = delete; 99 100 /// Move-assigns a ``UniquePtr<T>`` from a ``UniquePtr<U>``. 101 /// 102 /// This operation destructs and deallocates any value currently stored in 103 /// ``this``. 104 /// 105 /// This allows not only pure move assignment where ``T == U``, but also 106 /// converting assignment where ``T`` is a base class of ``U``, like 107 /// ``UniquePtr<Base> base = deallocator.MakeUnique<Child>();``. 108 template <typename U> 109 UniquePtr& operator=(UniquePtr<U>&& other) noexcept { 110 static_assert(std::is_assignable_v<UnderlyingType*&, 111 typename UniquePtr<U>::UnderlyingType*>, 112 "Attempted to assign a UniquePtr<U> to a UniquePtr<T> where " 113 "U* is not assignable to T*."); 114 Reset(); 115 value_ = other.value_; 116 deallocator_ = other.deallocator_; 117 size_ = other.size_; 118 other.Release(); 119 return *this; 120 } 121 122 /// Sets this ``UniquePtr`` to null, destructing and deallocating any 123 /// currently-held value. 124 /// 125 /// After this function returns, this ``UniquePtr`` will be in an "empty" 126 /// (``nullptr``) state until a new value is assigned. 127 UniquePtr& operator=(std::nullptr_t) { 128 Reset(); 129 return *this; 130 } 131 132 /// Destructs and deallocates any currently-held value. ~UniquePtr()133 ~UniquePtr() { Reset(); } 134 135 /// Returns a pointer to the object that can destroy the value. deallocator()136 Deallocator* deallocator() const { return deallocator_; } 137 138 /// Releases a value from the ``UniquePtr`` without destructing or 139 /// deallocating it. 140 /// 141 /// After this call, the object will have an "empty" (``nullptr``) value. Release()142 UnderlyingType* Release() { 143 UnderlyingType* value = value_; 144 value_ = nullptr; 145 deallocator_ = nullptr; 146 if constexpr (std::is_array_v<T>) { 147 size_ = 0; 148 } 149 return value; 150 } 151 152 /// Destructs and deallocates any currently-held value. 153 /// 154 /// After this function returns, this ``UniquePtr`` will be in an "empty" 155 /// (``nullptr``) state until a new value is assigned. Reset()156 void Reset() { 157 if (value_ == nullptr) { 158 return; 159 } 160 if (!Base::HasCapability(deallocator_, Capability::kSkipsDestroy)) { 161 if constexpr (std::is_array_v<T>) { 162 for (size_t i = 0; i < size_; ++i) { 163 std::destroy_at(value_ + i); 164 } 165 } else { 166 std::destroy_at(value_); 167 } 168 } 169 Base::Deallocate(deallocator_, value_); 170 Release(); 171 } 172 173 /// ``operator bool`` is not provided in order to ensure that there is no 174 /// confusion surrounding ``if (foo)`` vs. ``if (*foo)``. 175 /// 176 /// ``nullptr`` checking should instead use ``if (foo == nullptr)``. 177 explicit operator bool() const = delete; 178 179 /// Returns whether this ``UniquePtr`` is in an "empty" (``nullptr``) state. 180 bool operator==(std::nullptr_t) const { return value_ == nullptr; } 181 182 /// Returns whether this ``UniquePtr`` is not in an "empty" (``nullptr``) 183 /// state. 184 bool operator!=(std::nullptr_t) const { return value_ != nullptr; } 185 186 /// Returns the underlying (possibly null) pointer. get()187 UnderlyingType* get() { return value_; } 188 /// Returns the underlying (possibly null) pointer. get()189 const UnderlyingType* get() const { return value_; } 190 191 /// Permits accesses to members of ``T`` via ``my_unique_ptr->Member``. 192 /// 193 /// The behavior of this operation is undefined if this ``UniquePtr`` is in an 194 /// "empty" (``nullptr``) state. 195 UnderlyingType* operator->() { return value_; } 196 const UnderlyingType* operator->() const { return value_; } 197 198 /// Returns a reference to any underlying value. 199 /// 200 /// The behavior of this operation is undefined if this ``UniquePtr`` is in an 201 /// "empty" (``nullptr``) state. 202 UnderlyingType& operator*() { return *value_; } 203 const UnderlyingType& operator*() const { return *value_; } 204 205 /// Returns a reference to the element at the given index. 206 /// 207 /// The behavior of this operation is undefined if this ``UniquePtr`` is in an 208 /// "empty" (``nullptr``) state. 209 template <typename U = T, 210 typename = std::enable_if_t<std::is_array_v<U>, bool>> 211 UnderlyingType& operator[](size_t index) { 212 return value_[index]; 213 } 214 215 template <typename U = T, 216 typename = std::enable_if_t<std::is_array_v<U>, bool>> 217 const UnderlyingType& operator[](size_t index) const { 218 return value_[index]; 219 } 220 221 /// Returns the number of elements allocated. 222 /// 223 /// This will assert if it is called on a non-array type UniquePtr. 224 template <typename U = T, 225 typename = std::enable_if_t<std::is_array_v<U>, bool>> size()226 size_t size() const { 227 return size_; 228 } 229 230 private: 231 /// A pointer to the contained value. 232 UnderlyingType* value_; 233 234 /// The ``deallocator_`` which provided the memory for ``value_``. 235 /// This must be tracked in order to deallocate the memory upon destruction. 236 Deallocator* deallocator_; 237 238 /// The number of elements allocated. This will not be present in the case 239 /// where T is not an array type as this will be the empty struct type 240 /// optimized out. 241 PW_NO_UNIQUE_ADDRESS 242 std::conditional_t<std::is_array_v<T>, size_t, uniqueptr::internal::Empty> 243 size_; 244 245 /// Allow converting move constructor and assignment to access fields of 246 /// this class. 247 /// 248 /// Without this, ``UniquePtr<U>`` would not be able to access fields of 249 /// ``UniquePtr<T>``. 250 template <typename U> 251 friend class UniquePtr; 252 253 class PrivateConstructorType {}; 254 static constexpr PrivateConstructorType kPrivateConstructor{}; 255 256 public: 257 /// Private constructor that is public only for use with `emplace` and 258 /// other in-place construction functions. 259 /// 260 /// Constructs a ``UniquePtr`` from an already-allocated value. 261 /// 262 /// NOTE: Instances of this type are most commonly constructed using 263 /// ``Deallocator::MakeUnique``. UniquePtr(PrivateConstructorType,UnderlyingType * value,Deallocator * deallocator)264 UniquePtr(PrivateConstructorType, 265 UnderlyingType* value, 266 Deallocator* deallocator) 267 : value_(value), deallocator_(deallocator) {} 268 269 /// Private constructor that is public only for use with `emplace` and 270 /// other in-place construction functions. 271 /// 272 /// Constructs a ``UniquePtr`` from an already-allocated value and size. 273 /// 274 /// NOTE: Instances of this type are most commonly constructed using 275 /// ``Deallocator::MakeUnique``. UniquePtr(PrivateConstructorType,UnderlyingType * value,Deallocator * deallocator,size_t size)276 UniquePtr(PrivateConstructorType, 277 UnderlyingType* value, 278 Deallocator* deallocator, 279 size_t size) 280 : value_(value), deallocator_(deallocator), size_(size) {} 281 282 // Allow construction with ``kPrivateConstructor`` to the implementation 283 // of ``MakeUnique``. 284 friend class Deallocator; 285 }; 286 287 namespace allocator { 288 289 // Alias for module consumers using the older name for the above type. 290 template <typename T> 291 using UniquePtr = ::pw::UniquePtr<T>; 292 293 } // namespace allocator 294 } // namespace pw 295