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 <memory> 17 #include <utility> 18 19 namespace pw { 20 21 // Forward declaration. 22 class Deallocator; 23 24 namespace allocator::internal { 25 26 /// This class simply provides type-erased static methods to check capabilities 27 /// and deallocate memory in a unique pointer. This allows ``UniquePtr<T>`` to 28 /// be declared without a complete declaration of ``Deallocator``, breaking the 29 /// dependency cycle between ``UniquePtr<T>` and ``Allocator::MakeUnique<T>()``. 30 class BaseUniquePtr { 31 protected: 32 using Capability = ::pw::allocator::Capability; 33 34 static bool HasCapability(Deallocator* deallocator, Capability capability); 35 static void Deallocate(Deallocator* deallocator, void* ptr); 36 }; 37 38 } // namespace allocator::internal 39 40 /// An RAII pointer to a value of type ``T`` stored in memory provided by a 41 /// ``Deallocator``. 42 /// 43 /// This is analogous to ``std::unique_ptr``, but includes a few differences 44 /// in order to support ``Deallocator`` and encourage safe usage. Most 45 /// notably, ``UniquePtr<T>`` cannot be constructed from a ``T*``. 46 template <typename T> 47 class UniquePtr : public allocator::internal::BaseUniquePtr { 48 public: 49 using Base = ::pw::allocator::internal::BaseUniquePtr; 50 51 /// Creates an empty (``nullptr``) instance. 52 /// 53 /// NOTE: Instances of this type are most commonly constructed using 54 /// ``Deallocator::MakeUnique``. UniquePtr()55 constexpr UniquePtr() : value_(nullptr), deallocator_(nullptr) {} 56 57 /// Creates an empty (``nullptr``) instance. 58 /// 59 /// NOTE: Instances of this type are most commonly constructed using 60 /// ``Deallocator::MakeUnique``. UniquePtr(std::nullptr_t)61 constexpr UniquePtr(std::nullptr_t) : UniquePtr() {} 62 63 /// Move-constructs a ``UniquePtr<T>`` from a ``UniquePtr<U>``. 64 /// 65 /// This allows not only pure move construction where ``T == U``, but also 66 /// converting construction where ``T`` is a base class of ``U``, like 67 /// ``UniquePtr<Base> base(deallocator.MakeUnique<Child>());``. 68 template <typename U> UniquePtr(UniquePtr<U> && other)69 UniquePtr(UniquePtr<U>&& other) noexcept 70 : value_(other.value_), deallocator_(other.deallocator_) { 71 static_assert( 72 std::is_assignable_v<T*&, U*>, 73 "Attempted to construct a UniquePtr<T> from a UniquePtr<U> where " 74 "U* is not assignable to T*."); 75 other.Release(); 76 } 77 78 // Move-only. These are needed since the templated move-contructor and 79 // move-assignment operator do not exactly match the signature of the default 80 // move-contructor and move-assignment operator, and thus do not implicitly 81 // delete the copy-contructor and copy-assignment operator. 82 UniquePtr(const UniquePtr&) = delete; 83 UniquePtr& operator=(const UniquePtr&) = delete; 84 85 /// Move-assigns a ``UniquePtr<T>`` from a ``UniquePtr<U>``. 86 /// 87 /// This operation destructs and deallocates any value currently stored in 88 /// ``this``. 89 /// 90 /// This allows not only pure move assignment where ``T == U``, but also 91 /// converting assignment where ``T`` is a base class of ``U``, like 92 /// ``UniquePtr<Base> base = deallocator.MakeUnique<Child>();``. 93 template <typename U> 94 UniquePtr& operator=(UniquePtr<U>&& other) noexcept { 95 static_assert(std::is_assignable_v<T*&, U*>, 96 "Attempted to assign a UniquePtr<U> to a UniquePtr<T> where " 97 "U* is not assignable to T*."); 98 Reset(); 99 value_ = other.value_; 100 deallocator_ = other.deallocator_; 101 other.Release(); 102 return *this; 103 } 104 105 /// Sets this ``UniquePtr`` to null, destructing and deallocating any 106 /// currently-held value. 107 /// 108 /// After this function returns, this ``UniquePtr`` will be in an "empty" 109 /// (``nullptr``) state until a new value is assigned. 110 UniquePtr& operator=(std::nullptr_t) { 111 Reset(); 112 return *this; 113 } 114 115 /// Destructs and deallocates any currently-held value. ~UniquePtr()116 ~UniquePtr() { Reset(); } 117 118 /// Returns a pointer to the object that can destroy the value. deallocator()119 Deallocator* deallocator() const { return deallocator_; } 120 121 /// Releases a value from the ``UniquePtr`` without destructing or 122 /// deallocating it. 123 /// 124 /// After this call, the object will have an "empty" (``nullptr``) value. Release()125 T* Release() { 126 T* value = value_; 127 value_ = nullptr; 128 deallocator_ = nullptr; 129 return value; 130 } 131 132 /// Destructs and deallocates any currently-held value. 133 /// 134 /// After this function returns, this ``UniquePtr`` will be in an "empty" 135 /// (``nullptr``) state until a new value is assigned. Reset()136 void Reset() { 137 if (value_ == nullptr) { 138 return; 139 } 140 if (!Base::HasCapability(deallocator_, Capability::kSkipsDestroy)) { 141 std::destroy_at(value_); 142 } 143 Base::Deallocate(deallocator_, value_); 144 Release(); 145 } 146 147 /// ``operator bool`` is not provided in order to ensure that there is no 148 /// confusion surrounding ``if (foo)`` vs. ``if (*foo)``. 149 /// 150 /// ``nullptr`` checking should instead use ``if (foo == nullptr)``. 151 explicit operator bool() const = delete; 152 153 /// Returns whether this ``UniquePtr`` is in an "empty" (``nullptr``) state. 154 bool operator==(std::nullptr_t) const { return value_ == nullptr; } 155 156 /// Returns whether this ``UniquePtr`` is not in an "empty" (``nullptr``) 157 /// state. 158 bool operator!=(std::nullptr_t) const { return value_ != nullptr; } 159 160 /// Returns the underlying (possibly null) pointer. get()161 T* get() { return value_; } 162 /// Returns the underlying (possibly null) pointer. get()163 const T* get() const { return value_; } 164 165 /// Permits accesses to members of ``T`` via ``my_unique_ptr->Member``. 166 /// 167 /// The behavior of this operation is undefined if this ``UniquePtr`` is in an 168 /// "empty" (``nullptr``) state. 169 T* operator->() { return value_; } 170 const T* operator->() const { return value_; } 171 172 /// Returns a reference to any underlying value. 173 /// 174 /// The behavior of this operation is undefined if this ``UniquePtr`` is in an 175 /// "empty" (``nullptr``) state. 176 T& operator*() { return *value_; } 177 const T& operator*() const { return *value_; } 178 179 private: 180 /// A pointer to the contained value. 181 T* value_; 182 183 /// The ``deallocator_`` which provided the memory for ``value_``. 184 /// This must be tracked in order to deallocate the memory upon destruction. 185 Deallocator* deallocator_; 186 187 /// Allow converting move constructor and assignment to access fields of 188 /// this class. 189 /// 190 /// Without this, ``UniquePtr<U>`` would not be able to access fields of 191 /// ``UniquePtr<T>``. 192 template <typename U> 193 friend class UniquePtr; 194 195 class PrivateConstructorType {}; 196 static constexpr PrivateConstructorType kPrivateConstructor{}; 197 198 public: 199 /// Private constructor that is public only for use with `emplace` and 200 /// other in-place construction functions. 201 /// 202 /// Constructs a ``UniquePtr`` from an already-allocated value. 203 /// 204 /// NOTE: Instances of this type are most commonly constructed using 205 /// ``Deallocator::MakeUnique``. UniquePtr(PrivateConstructorType,T * value,Deallocator * deallocator)206 UniquePtr(PrivateConstructorType, T* value, Deallocator* deallocator) 207 : value_(value), deallocator_(deallocator) {} 208 209 // Allow construction with ``kPrivateConstructor`` to the implementation 210 // of ``MakeUnique``. 211 friend class Deallocator; 212 }; 213 214 namespace allocator { 215 216 // Alias for module consumers using the older name for the above type. 217 template <typename T> 218 using UniquePtr = ::pw::UniquePtr<T>; 219 220 } // namespace allocator 221 } // namespace pw 222