C/C++ API Reference
Loading...
Searching...
No Matches
unique_ptr.h
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 <type_traits>
18#include <utility>
19
20#include "pw_allocator/config.h"
21#include "pw_allocator/deallocator.h"
22#include "pw_allocator/internal/managed_ptr.h"
23#include "pw_allocator/layout.h"
24#include "pw_preprocessor/compiler.h"
25
26namespace pw {
27
29
43template <typename T>
44class UniquePtr : public ::pw::allocator::internal::ManagedPtr<T> {
45 private:
46 using Base = ::pw::allocator::internal::ManagedPtr<T>;
47 using Empty = ::pw::allocator::internal::Empty;
48
49 public:
50 using pointer = typename Base::element_type*;
51 using element_type = typename Base::element_type;
52
57 constexpr UniquePtr() noexcept;
58
59 // Not copyable.
60 UniquePtr(const UniquePtr&) = delete;
61 UniquePtr& operator=(const UniquePtr&) = delete;
62
67 constexpr UniquePtr(std::nullptr_t) noexcept : UniquePtr() {}
68
83 UniquePtr(element_type* value, Deallocator& deallocator);
84 UniquePtr(element_type* value, size_t size, Deallocator& deallocator);
86
92 template <typename U,
93 typename = std::enable_if_t<std::is_assignable_v<T*&, U*>>>
94 UniquePtr(UniquePtr<U>&& other) noexcept {
95 *this = std::move(other);
96 }
97
100
107 template <typename U,
108 typename = std::enable_if_t<std::is_assignable_v<T*&, U*>>>
109 UniquePtr& operator=(UniquePtr<U>&& other) noexcept;
110
115 UniquePtr& operator=(std::nullptr_t) noexcept;
116
128 template <typename U,
129 std::enable_if_t<std::is_assignable_v<T*&, U*>, int> = 0>
130 constexpr explicit operator UniquePtr<U>() &&;
131
132 [[nodiscard]] friend constexpr bool operator==(const UniquePtr& lhs,
133 std::nullptr_t) {
134 return lhs.Equals(nullptr);
135 }
136 [[nodiscard]] friend constexpr bool operator==(std::nullptr_t,
137 const UniquePtr& rhs) {
138 return rhs.Equals(nullptr);
139 }
140 [[nodiscard]] friend constexpr bool operator==(const UniquePtr& lhs,
141 const UniquePtr& rhs) {
142 return lhs.Equals(rhs) && lhs.control_block_ == rhs.control_block_;
143 }
144
145 [[nodiscard]] friend constexpr bool operator!=(const UniquePtr& lhs,
146 std::nullptr_t) {
147 return !lhs.Equals(nullptr);
148 }
149 [[nodiscard]] friend constexpr bool operator!=(std::nullptr_t,
150 const UniquePtr& rhs) {
151 return !rhs.Equals(nullptr);
152 }
153 [[nodiscard]] friend constexpr bool operator!=(const UniquePtr& lhs,
154 const UniquePtr& rhs) {
155 return !(lhs == rhs);
156 }
157
161 size_t size() const;
162
164 Deallocator* deallocator() const { return deallocator_; }
165
170 element_type* Release() noexcept;
171
176 void Reset() noexcept;
177
179 void Swap(UniquePtr& other);
180
181 private:
182 // Allow construction with to the implementation of `MakeUnique`.
183 friend class Deallocator;
184
185 // Allow UniquePtr<T> to access UniquePtr<U> and vice versa.
186 template <typename>
187 friend class UniquePtr;
188
190 template <typename U,
191 typename = std::enable_if_t<std::is_assignable_v<T*&, U*>>>
192 void CopyFrom(const UniquePtr<U>& other);
193
198 std::conditional_t<std::is_array_v<T>, size_t, Empty> size_;
199
202 Deallocator* deallocator_ = nullptr;
203};
204
206
207// Template method implementations.
208
209template <typename T>
210constexpr UniquePtr<T>::UniquePtr() noexcept {
211 if constexpr (std::is_array_v<T>) {
212 size_ = 0;
213 }
214}
215
216template <typename T>
218 : Base(value), deallocator_(&deallocator) {
219 static_assert(!is_unbounded_array_v<T>,
220 "UniquePtr for unbounded array type must provide size");
221 if constexpr (is_bounded_array_v<T>) {
222 size_ = std::extent_v<T>;
223 }
224}
225
226template <typename T>
227UniquePtr<T>::UniquePtr(element_type* value,
228 size_t size,
229 Deallocator& deallocator)
230 : Base(value), size_(size), deallocator_(&deallocator) {
231 static_assert(
232 is_unbounded_array_v<T>,
233 "UniquePtr must not provide size unless type is an unbounded array");
234}
235
236template <typename T>
237template <typename U, std::enable_if_t<std::is_assignable_v<T*&, U*>, int>>
238constexpr UniquePtr<T>::operator UniquePtr<U>() && {
239 Deallocator& deallocator = *deallocator_;
240 return UniquePtr<U>(static_cast<U*>(Release()), deallocator);
241}
242
243template <typename T>
244size_t UniquePtr<T>::size() const {
245 static_assert(std::is_array_v<T>,
246 "size() cannot be called with a non-array type");
247 return size_;
248}
249
250template <typename T>
251template <typename U, typename>
253 Reset();
254 CopyFrom(other);
255 other.Release();
256 return *this;
257}
258
259template <typename T>
260UniquePtr<T>& UniquePtr<T>::operator=(std::nullptr_t) noexcept {
261 Reset();
262 return *this;
263}
264
265template <typename T>
266typename UniquePtr<T>::element_type* UniquePtr<T>::Release() noexcept {
267 element_type* value = Base::Release();
268 deallocator_ = nullptr;
269 if constexpr (std::is_array_v<T>) {
270 size_ = 0;
271 }
272 return value;
273}
274
275template <typename T>
276void UniquePtr<T>::Reset() noexcept {
277 if (*this == nullptr) {
278 return;
279 }
280 if (!deallocator_->HasCapability(allocator::kSkipsDestroy)) {
281 if constexpr (std::is_array_v<T>) {
282 Base::Destroy(size_);
283 size_ = 0;
284 } else {
285 Base::Destroy();
286 }
287 }
288 Deallocator* deallocator = deallocator_;
289 auto* ptr = const_cast<std::remove_const_t<element_type>*>(Release());
290 deallocator->Deallocate(ptr);
291}
292
293template <typename T>
295 Base::Swap(other);
296 if constexpr (std::is_array_v<T>) {
297 std::swap(size_, other.size_);
298 }
299 std::swap(deallocator_, other.deallocator_);
300}
301
302template <typename T>
303template <typename U, typename>
304void UniquePtr<T>::CopyFrom(const UniquePtr<U>& other) {
305 static_assert(std::is_array_v<T> == std::is_array_v<U>);
306 Base::CopyFrom(other);
307 if constexpr (std::is_array_v<T>) {
308 size_ = other.size_;
309 }
310 deallocator_ = other.deallocator_;
311}
312
313} // namespace pw
Abstract interface for releasing memory.
Definition: deallocator.h:30
Definition: unique_ptr.h:44
element_type * Release() noexcept
Definition: unique_ptr.h:266
Deallocator * deallocator() const
Returns a pointer to the object that can destroy the value.
Definition: unique_ptr.h:164
UniquePtr & operator=(UniquePtr< U > &&other) noexcept
void Reset() noexcept
Definition: unique_ptr.h:276
size_t size() const
Definition: unique_ptr.h:244
~UniquePtr()
Frees any currently-held value.
Definition: unique_ptr.h:99
constexpr UniquePtr(std::nullptr_t) noexcept
Definition: unique_ptr.h:67
void Swap(UniquePtr &other)
Swaps the managed pointer and deallocator of this and another object.
Definition: unique_ptr.h:294
UniquePtr(UniquePtr< U > &&other) noexcept
Definition: unique_ptr.h:94
void Deallocate(void *ptr)
Definition: deallocator.h:314
constexpr Deallocator()=default
TODO(b/326509341): Remove when downstream consumers migrate.
Definition: unique_ptr.h:183
#define PW_NO_UNIQUE_ADDRESS
Definition: compiler.h:297
The Pigweed namespace.
Definition: alignment.h:27