xref: /aosp_15_r20/external/pigweed/pw_allocator/public/pw_allocator/unique_ptr.h (revision 61c4878ac05f98d0ceed94b57d316916de578985)
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