C/C++ API Reference
Loading...
Searching...
No Matches
shared_ptr.h
1// Copyright 2025 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 "pw_allocator/config.h"
17
18// TODO(b/402489948): Remove when portable atomics are provided by `pw_atomic`.
19#if PW_ALLOCATOR_HAS_ATOMICS
20
21#include <cstddef>
22#include <cstdint>
23#include <utility>
24
25#include "pw_allocator/deallocator.h"
26#include "pw_allocator/internal/control_block.h"
27#include "pw_allocator/internal/managed_ptr.h"
28#include "pw_allocator/layout.h"
29#include "pw_allocator/unique_ptr.h"
30
31namespace pw {
32
34
35// Forward declarations.
36template <typename T>
37class WeakPtr;
38
39enum class MultiBufProperty : uint8_t;
40
41template <MultiBufProperty...>
42class BasicMultiBuf;
43
59template <typename T>
60class SharedPtr final : public ::pw::allocator::internal::ManagedPtr<T> {
61 private:
62 using Base = ::pw::allocator::internal::ManagedPtr<T>;
63 using ControlBlock = ::pw::allocator::internal::ControlBlock;
64
65 public:
66 using element_type = typename Base::element_type;
67 using weak_type = WeakPtr<T>;
68
70 ~SharedPtr() { reset(); }
71
76 constexpr SharedPtr() noexcept = default;
77
82 constexpr SharedPtr(std::nullptr_t) noexcept : SharedPtr() {}
83
85 constexpr SharedPtr(const SharedPtr& other) noexcept : SharedPtr() {
86 *this = other;
87 }
88
93 template <typename U,
94 typename = std::enable_if_t<std::is_assignable_v<T*&, U*>>>
95 constexpr SharedPtr(const SharedPtr<U>& other) noexcept : SharedPtr() {
96 *this = other;
97 }
98
104 template <typename U,
105 typename = std::enable_if_t<std::is_assignable_v<T*&, U*>>>
106 SharedPtr(SharedPtr<U>&& other) noexcept : SharedPtr() {
107 *this = std::move(other);
108 }
109
119 SharedPtr(UniquePtr<T>& owned) noexcept;
120
124 constexpr SharedPtr& operator=(const SharedPtr& other) noexcept {
125 operator= <T>(other);
126 return *this;
127 }
128
135 template <typename U,
136 typename = std::enable_if_t<std::is_assignable_v<T*&, U*>>>
137 constexpr SharedPtr& operator=(const SharedPtr<U>& other) noexcept;
138
145 template <typename U,
146 typename = std::enable_if_t<std::is_assignable_v<T*&, U*>>>
147 SharedPtr& operator=(SharedPtr<U>&& other) noexcept;
148
153 SharedPtr& operator=(std::nullptr_t) noexcept;
154
166 template <typename U,
167 typename = std::enable_if_t<std::is_assignable_v<T*&, U*>>>
168 constexpr explicit operator const SharedPtr<U>&() const {
169 return static_cast<const SharedPtr<U>&>(
170 static_cast<const allocator::internal::BaseManagedPtr&>(*this));
171 }
172
173 [[nodiscard]] friend constexpr bool operator==(const SharedPtr& lhs,
174 std::nullptr_t) {
175 return lhs.Equals(nullptr);
176 }
177 [[nodiscard]] friend constexpr bool operator==(std::nullptr_t,
178 const SharedPtr& rhs) {
179 return rhs.Equals(nullptr);
180 }
181 [[nodiscard]] friend constexpr bool operator==(const SharedPtr& lhs,
182 const SharedPtr& rhs) {
183 return lhs.Equals(rhs) && lhs.control_block_ == rhs.control_block_;
184 }
185
186 [[nodiscard]] friend constexpr bool operator!=(const SharedPtr& lhs,
187 std::nullptr_t) {
188 return !lhs.Equals(nullptr);
189 }
190 [[nodiscard]] friend constexpr bool operator!=(std::nullptr_t,
191 const SharedPtr& rhs) {
192 return !rhs.Equals(nullptr);
193 }
194 [[nodiscard]] friend constexpr bool operator!=(const SharedPtr& lhs,
195 const SharedPtr& rhs) {
196 return !(lhs == rhs);
197 }
198
202 size_t size() const {
203 static_assert(std::is_array_v<T>,
204 "size() cannot be called on non-array types");
205 return control_block_ == nullptr ? 0 : control_block_->size();
206 }
207
210 int32_t use_count() const {
211 return control_block_ == nullptr ? 0 : control_block_->num_shared();
212 }
213
219 void reset() noexcept;
220
222 void swap(SharedPtr& other) noexcept;
223
226 template <typename PtrType>
227 bool owner_before(const PtrType& other) const noexcept {
228 return control_block_ < other.control_block();
229 }
230
231 private:
232 using Layout = allocator::Layout;
233
234 static constexpr bool is_unbounded_array_v =
235 allocator::internal::is_unbounded_array_v<T>;
236
237 // Allow construction with to the implementation of `MakeShared`.
238 friend class Allocator;
239
240 // Allow SharedPtr<T> to access SharedPtr<U> and vice versa.
241 template <typename>
242 friend class SharedPtr;
243
244 // Allow WeakPtr<T> to promote to a SharedPtr<T>.
245 template <typename>
246 friend class WeakPtr;
247
248 // Allow MultiBufs to decompose SharedPtr<T>.
249 template <MultiBufProperty...>
250 friend class BasicMultiBuf;
251
262 template <typename... Args>
263 static SharedPtr Create(Allocator* allocator, Args&&... args);
264
276 static SharedPtr Create(Allocator* allocator, size_t count, size_t alignment);
277
282 SharedPtr(element_type* value, ControlBlock* control_block)
283 : Base(value), control_block_(control_block) {}
284
285 ControlBlock* control_block() const { return control_block_; }
286
288 template <typename U,
289 typename = std::enable_if_t<std::is_assignable_v<T*&, U*>>>
290 void CopyFrom(const SharedPtr<U>& other);
291
294 void Release();
295
297 template <typename U>
298 constexpr void CheckArrayTypes();
299
300 ControlBlock* control_block_ = nullptr;
301};
302
304
305// Template method implementations.
306
307template <typename T>
309 if constexpr (std::is_array_v<T>) {
310 control_block_ =
311 ControlBlock::Create(owned.deallocator(), owned.get(), owned.size());
312 } else {
313 control_block_ = ControlBlock::Create(owned.deallocator(), owned.get(), 1);
314 }
315 if (control_block_ != nullptr) {
316 Base::CopyFrom(owned);
317 owned.Release();
318 }
319}
320
321template <typename T>
322template <typename... Args>
323SharedPtr<T> SharedPtr<T>::Create(Allocator* allocator, Args&&... args) {
324 static_assert(!std::is_array_v<T>);
325 auto* control_block = ControlBlock::Create(allocator, Layout::Of<T>(), 1);
326 if (control_block == nullptr) {
327 return nullptr;
328 }
329 auto* t = new (control_block->data()) T(std::forward<Args>(args)...);
330 return SharedPtr<T>(t, control_block);
331}
332
333template <typename T>
334SharedPtr<T> SharedPtr<T>::Create(Allocator* allocator,
335 size_t count,
336 size_t alignment) {
337 static_assert(allocator::internal::is_unbounded_array_v<T>);
338 Layout layout = Layout::Of<T>(count).Align(alignment);
339 auto* control_block = ControlBlock::Create(allocator, layout, count);
340 if (control_block == nullptr) {
341 return nullptr;
342 }
343 auto* t = new (control_block->data()) std::remove_extent_t<T>[count];
344 return SharedPtr<T>(t, control_block);
345}
346
347template <typename T>
348template <typename U, typename>
349constexpr SharedPtr<T>& SharedPtr<T>::operator=(
350 const SharedPtr<U>& other) noexcept {
351 CheckArrayTypes<U>();
352 CopyFrom(other);
353 if (control_block_ != nullptr) {
354 control_block_->IncrementShared();
355 }
356 return *this;
357}
358
359template <typename T>
360template <typename U, typename>
361SharedPtr<T>& SharedPtr<T>::operator=(SharedPtr<U>&& other) noexcept {
362 CheckArrayTypes<U>();
363 reset();
364 CopyFrom(other);
365 other.Release();
366 return *this;
367}
368
369template <typename T>
370SharedPtr<T>& SharedPtr<T>::operator=(std::nullptr_t) noexcept {
371 reset();
372 return *this;
373}
374
375template <typename T>
376void SharedPtr<T>::reset() noexcept {
377 if (*this == nullptr) {
378 return;
379 }
380 auto action = control_block_->DecrementShared();
381 if (action == ControlBlock::Action::kNone) {
382 // Other `SharedPtr`s associated with this control block remain.
383 Release();
384 return;
385 }
386
387 // This was the last `SharedPtr` associated with this control block.
388 Allocator* allocator = control_block_->allocator();
389 if (!Base::HasCapability(allocator, allocator::kSkipsDestroy)) {
390 if constexpr (std::is_array_v<T>) {
391 Base::Destroy(control_block_->size());
392 } else {
393 Base::Destroy();
394 }
395 }
396
397 if (action == ControlBlock::Action::kExpire) {
398 // Keep the control block. Trying to promote any of the remaining
399 // `WeakPtr`s will fail.
400 Base::Resize(allocator, control_block_, sizeof(ControlBlock));
401 } else {
402 // No `WeakPtr`s remain, and all of the memory can be freed.
403 Base::Deallocate(allocator, control_block_);
404 }
405
406 Release();
407}
408
409template <typename T>
410void SharedPtr<T>::swap(SharedPtr<T>& other) noexcept {
411 Base::Swap(other);
412 std::swap(control_block_, other.control_block_);
413}
414
415template <typename T>
416template <typename U, typename>
417void SharedPtr<T>::CopyFrom(const SharedPtr<U>& other) {
418 CheckArrayTypes<U>();
419 Base::CopyFrom(other);
420 control_block_ = other.control_block_;
421}
422
423template <typename T>
424void SharedPtr<T>::Release() {
425 Base::Release();
426 control_block_ = nullptr;
427}
428
429template <typename T>
430template <typename U>
431constexpr void SharedPtr<T>::CheckArrayTypes() {
432 if constexpr (std::is_array_v<T>) {
433 static_assert(std::is_array_v<U>,
434 "non-array type used with SharedPtr<T[]>");
435 } else {
436 static_assert(!std::is_array_v<U>, "array type used with SharedPtr<T>");
437 }
438}
439
440} // namespace pw
441
442// TODO(b/402489948): Remove when portable atomics are provided by `pw_atomic`.
443#endif // PW_ALLOCATOR_HAS_ATOMICS
Definition: allocator.h:36
Definition: multibuf_v2.h:184
Definition: shared_ptr.h:60
size_t size() const
Definition: shared_ptr.h:202
void swap(SharedPtr &other) noexcept
Swaps the managed pointer and deallocator of this and another object.
Definition: shared_ptr.h:410
constexpr SharedPtr & operator=(const SharedPtr< U > &other) noexcept
SharedPtr & operator=(SharedPtr< U > &&other) noexcept
SharedPtr(SharedPtr< U > &&other) noexcept
Definition: shared_ptr.h:106
~SharedPtr()
Releases any currently-held value.
Definition: shared_ptr.h:70
constexpr SharedPtr() noexcept=default
constexpr SharedPtr(const SharedPtr &other) noexcept
Copy-constructs a SharedPtr<T> from a SharedPtr<T>.
Definition: shared_ptr.h:85
int32_t use_count() const
Definition: shared_ptr.h:210
constexpr SharedPtr(const SharedPtr< U > &other) noexcept
Definition: shared_ptr.h:95
constexpr SharedPtr & operator=(const SharedPtr &other) noexcept
Definition: shared_ptr.h:124
void reset() noexcept
Definition: shared_ptr.h:376
Definition: unique_ptr.h:43
Definition: weak_ptr.h:37
Definition: layout.h:58
MultiBufProperty
Basic properties of a MultiBuf.
Definition: properties.h:24
The Pigweed namespace.
Definition: alignment.h:27