• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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