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 SharedPtr;
38
39template <typename To, typename From>
40constexpr SharedPtr<To> static_pointer_cast(const SharedPtr<From>& p) noexcept;
41
42template <typename To, typename From>
43constexpr SharedPtr<To> const_pointer_cast(const SharedPtr<From>& p) noexcept;
44
45template <typename T>
46class WeakPtr;
47
63template <typename T>
64class SharedPtr final : public ::pw::allocator::internal::ManagedPtr<T> {
65 private:
66 using Base = ::pw::allocator::internal::ManagedPtr<T>;
67 using ControlBlock = ::pw::allocator::internal::ControlBlock;
68 using ControlBlockHandle = ::pw::allocator::internal::ControlBlockHandle;
69
70 public:
71 using element_type = typename Base::element_type;
72 using weak_type = WeakPtr<T>;
73
76
87 template <typename... Args>
88 static SharedPtr Create(Allocator* allocator, Args&&... args);
89
101 static SharedPtr Create(Allocator* allocator, size_t count, size_t alignment);
102
107 constexpr SharedPtr() noexcept = default;
108
113 constexpr SharedPtr(element_type* value, ControlBlock* control_block)
114 : Base(value), control_block_(control_block) {}
115
120 constexpr SharedPtr(std::nullptr_t) noexcept : SharedPtr() {}
121
123 constexpr SharedPtr(const SharedPtr& other) noexcept : SharedPtr() {
124 *this = other;
125 }
126
131 template <typename U,
132 typename = std::enable_if_t<std::is_assignable_v<T*&, U*>>>
133 constexpr SharedPtr(const SharedPtr<U>& other) noexcept : SharedPtr() {
134 *this = other;
135 }
136
142 template <typename U,
143 typename = std::enable_if_t<std::is_assignable_v<T*&, U*>>>
144 SharedPtr(SharedPtr<U>&& other) noexcept : SharedPtr() {
145 *this = std::move(other);
146 }
147
157 SharedPtr(UniquePtr<T>& owned) noexcept;
158
162 constexpr SharedPtr& operator=(const SharedPtr& other) noexcept {
163 operator= <T>(other);
164 return *this;
165 }
166
173 template <typename U,
174 typename = std::enable_if_t<std::is_assignable_v<T*&, U*>>>
175 constexpr SharedPtr& operator=(const SharedPtr<U>& other) noexcept;
176
183 template <typename U,
184 typename = std::enable_if_t<std::is_assignable_v<T*&, U*>>>
185 SharedPtr& operator=(SharedPtr<U>&& other) noexcept;
186
191 SharedPtr& operator=(std::nullptr_t) noexcept;
192
193 // Conversions
194
206 template <typename U>
207 constexpr explicit operator SharedPtr<U>() const {
208 return static_pointer_cast<U>(*this);
209 }
210
223 template <typename To, typename From>
225 const SharedPtr<From>& p) noexcept;
226
237 template <typename To, typename From>
239 const SharedPtr<From>& p) noexcept;
240
241 // Comparisons
242
243 [[nodiscard]] friend constexpr bool operator==(const SharedPtr& lhs,
244 std::nullptr_t) {
245 return lhs.Equals(nullptr);
246 }
247 [[nodiscard]] friend constexpr bool operator==(std::nullptr_t,
248 const SharedPtr& rhs) {
249 return rhs.Equals(nullptr);
250 }
251 [[nodiscard]] friend constexpr bool operator==(const SharedPtr& lhs,
252 const SharedPtr& rhs) {
253 return lhs.Equals(rhs) && lhs.control_block_ == rhs.control_block_;
254 }
255
256 [[nodiscard]] friend constexpr bool operator!=(const SharedPtr& lhs,
257 std::nullptr_t) {
258 return !lhs.Equals(nullptr);
259 }
260 [[nodiscard]] friend constexpr bool operator!=(std::nullptr_t,
261 const SharedPtr& rhs) {
262 return !rhs.Equals(nullptr);
263 }
264 [[nodiscard]] friend constexpr bool operator!=(const SharedPtr& lhs,
265 const SharedPtr& rhs) {
266 return !(lhs == rhs);
267 }
268
269 // Accessors
270
274 constexpr size_t size() const {
275 static_assert(std::is_array_v<T>,
276 "size() cannot be called on non-array types");
277 return control_block_ == nullptr
278 ? 0
279 : (control_block_->size() / sizeof(element_type));
280 }
281
284 constexpr int32_t use_count() const {
285 return control_block_ == nullptr ? 0 : control_block_->num_shared();
286 }
287
290 template <typename PtrType>
291 bool owner_before(const PtrType& other) const noexcept {
292 return std::less{}(control_block_, other.control_block_);
293 }
294
295 // Mutators
296
302 void reset() noexcept;
303
305 void swap(SharedPtr& other) noexcept;
306
310 return control_block_ == nullptr ? nullptr : control_block_->allocator();
311 }
312
317 ControlBlock* GetControlBlock(const ControlBlockHandle&) const {
318 return control_block_;
319 }
320
321 private:
322 using Layout = allocator::Layout;
323
324 // Allow other SharedPtrs and WeakPtrs to access control blocks directly.
325 template <typename>
326 friend class SharedPtr;
327
328 template <typename>
329 friend class WeakPtr;
330
332 template <typename U,
333 typename = std::enable_if_t<std::is_assignable_v<T*&, U*>>>
334 void CopyFrom(const SharedPtr<U>& other);
335
338 void Release();
339
341 template <typename U>
342 constexpr void CheckArrayTypes();
343
344 ControlBlock* control_block_ = nullptr;
345};
346
348
349// Template method implementations.
350
351template <typename T>
353 size_t size = sizeof(element_type);
354 if constexpr (std::is_array_v<T>) {
355 size *= owned.size();
356 }
357
358 // Create the shared pointer's control block using the restricted method.
359 const auto& handle = ControlBlockHandle::GetInstance_DO_NOT_USE();
360 control_block_ =
361 ControlBlock::Create(handle, owned.deallocator(), owned.get(), size);
362
363 if (control_block_ != nullptr) {
364 Base::CopyFrom(owned);
365 owned.Release();
366 }
367}
368
369template <typename T>
370template <typename... Args>
371SharedPtr<T> SharedPtr<T>::Create(Allocator* allocator, Args&&... args) {
372 static_assert(!std::is_array_v<T>);
373
374 // Create the shared pointer's control block using the restricted method.
375 const auto& handle = ControlBlockHandle::GetInstance_DO_NOT_USE();
376 auto* control_block =
377 ControlBlock::Create(handle, allocator, Layout::Of<T>());
378
379 if (control_block == nullptr) {
380 return nullptr;
381 }
382 auto* t = new (control_block->data()) T(std::forward<Args>(args)...);
383 return SharedPtr<T>(t, control_block);
384}
385
386template <typename T>
388 size_t count,
389 size_t alignment) {
390 static_assert(allocator::internal::is_unbounded_array_v<T>);
391 Layout layout = Layout::Of<T>(count).Align(alignment);
392
393 // Create the shared pointer's control block using the restricted method.
394 const auto& handle = ControlBlockHandle::GetInstance_DO_NOT_USE();
395 auto* control_block = ControlBlock::Create(handle, allocator, layout);
396
397 if (control_block == nullptr) {
398 return nullptr;
399 }
400 auto* t = new (control_block->data()) element_type[count];
401 return SharedPtr<T>(t, control_block);
402}
403
404template <typename T>
405template <typename U, typename>
407 const SharedPtr<U>& other) noexcept {
408 CheckArrayTypes<U>();
409 reset();
410 CopyFrom(other);
411 if (control_block_ != nullptr) {
412 control_block_->IncrementShared();
413 }
414 return *this;
415}
416
417template <typename T>
418template <typename U, typename>
419SharedPtr<T>& SharedPtr<T>::operator=(SharedPtr<U>&& other) noexcept {
420 CheckArrayTypes<U>();
421 reset();
422 CopyFrom(other);
423 other.Release();
424 return *this;
425}
426
427template <typename T>
428SharedPtr<T>& SharedPtr<T>::operator=(std::nullptr_t) noexcept {
429 reset();
430 return *this;
431}
432
433template <typename To, typename From>
435 using PtrType = typename SharedPtr<To>::element_type*;
436 if (p.control_block_ != nullptr) {
437 p.control_block_->IncrementShared();
438 }
439 return SharedPtr<To>{static_cast<PtrType>(p.get()), p.control_block_};
440}
441
442template <typename To, typename From>
444 using PtrType = typename SharedPtr<To>::element_type*;
445 if (p.control_block_ != nullptr) {
446 p.control_block_->IncrementShared();
447 }
448 return SharedPtr<To>{const_cast<PtrType>(p.get()), p.control_block_};
449}
450
451template <typename T>
452void SharedPtr<T>::reset() noexcept {
453 if (*this == nullptr) {
454 return;
455 }
456 auto action = control_block_->DecrementShared();
457 if (action == ControlBlock::Action::kNone) {
458 // Other `SharedPtr`s associated with this control block remain.
459 Release();
460 return;
461 }
462
463 // This was the last `SharedPtr` associated with this control block.
464 Allocator* allocator = control_block_->allocator();
465 if (!Base::HasCapability(allocator, allocator::kSkipsDestroy)) {
466 if constexpr (std::is_array_v<T>) {
467 Base::Destroy(size());
468 } else {
469 Base::Destroy();
470 }
471 }
472
473 if (action == ControlBlock::Action::kExpire) {
474 // Keep the control block. Trying to promote any of the remaining
475 // `WeakPtr`s will fail.
476 Base::Resize(allocator, control_block_, sizeof(ControlBlock));
477 } else {
478 // No `WeakPtr`s remain, and all of the memory can be freed.
479 std::destroy_at(control_block_);
480 Base::Deallocate(allocator, control_block_);
481 }
482
483 Release();
484}
485
486template <typename T>
487void SharedPtr<T>::swap(SharedPtr<T>& other) noexcept {
488 Base::Swap(other);
489 std::swap(control_block_, other.control_block_);
490}
491
492template <typename T>
493template <typename U, typename>
494void SharedPtr<T>::CopyFrom(const SharedPtr<U>& other) {
495 CheckArrayTypes<U>();
496 Base::CopyFrom(other);
497 control_block_ = other.control_block_;
498}
499
500template <typename T>
501void SharedPtr<T>::Release() {
502 Base::Release();
503 control_block_ = nullptr;
504}
505
506template <typename T>
507template <typename U>
508constexpr void SharedPtr<T>::CheckArrayTypes() {
509 if constexpr (std::is_array_v<T>) {
510 static_assert(std::is_array_v<U>,
511 "non-array type used with SharedPtr<T[]>");
512 } else {
513 static_assert(!std::is_array_v<U>, "array type used with SharedPtr<T>");
514 }
515}
516
517} // namespace pw
518
519// TODO(b/402489948): Remove when portable atomics are provided by `pw_atomic`.
520#endif // PW_ALLOCATOR_HAS_ATOMICS
Definition: allocator.h:42
Definition: shared_ptr.h:64
static SharedPtr Create(Allocator *allocator, size_t count, size_t alignment)
Definition: shared_ptr.h:387
bool owner_before(const PtrType &other) const noexcept
Definition: shared_ptr.h:291
SharedPtr(UniquePtr< T > &owned) noexcept
Definition: shared_ptr.h:352
constexpr SharedPtr(std::nullptr_t) noexcept
Definition: shared_ptr.h:120
friend constexpr SharedPtr< To > const_pointer_cast(const SharedPtr< From > &p) noexcept
Definition: shared_ptr.h:443
void swap(SharedPtr &other) noexcept
Swaps the managed pointer and deallocator of this and another object.
Definition: shared_ptr.h:487
constexpr int32_t use_count() const
Definition: shared_ptr.h:284
SharedPtr & operator=(std::nullptr_t) noexcept
Definition: shared_ptr.h:428
constexpr SharedPtr & operator=(const SharedPtr< U > &other) noexcept
SharedPtr & operator=(SharedPtr< U > &&other) noexcept
static SharedPtr Create(Allocator *allocator, Args &&... args)
SharedPtr(SharedPtr< U > &&other) noexcept
Definition: shared_ptr.h:144
~SharedPtr()
Releases any currently-held value.
Definition: shared_ptr.h:75
constexpr size_t size() const
Definition: shared_ptr.h:274
friend constexpr SharedPtr< To > static_pointer_cast(const SharedPtr< From > &p) noexcept
Definition: shared_ptr.h:434
ControlBlock * GetControlBlock(const ControlBlockHandle &) const
Definition: shared_ptr.h:317
constexpr SharedPtr() noexcept=default
constexpr SharedPtr(const SharedPtr &other) noexcept
Copy-constructs a SharedPtr<T> from a SharedPtr<T>.
Definition: shared_ptr.h:123
Allocator * allocator() const
Definition: shared_ptr.h:309
constexpr SharedPtr(const SharedPtr< U > &other) noexcept
Definition: shared_ptr.h:133
constexpr SharedPtr & operator=(const SharedPtr &other) noexcept
Definition: shared_ptr.h:162
void reset() noexcept
Definition: shared_ptr.h:452
Definition: unique_ptr.h:44
Definition: weak_ptr.h:37
Definition: layout.h:58
constexpr SharedPtr< To > static_pointer_cast(const SharedPtr< From > &p) noexcept
Definition: shared_ptr.h:434
constexpr SharedPtr< To > const_pointer_cast(const SharedPtr< From > &p) noexcept
Definition: shared_ptr.h:443
The Pigweed namespace.
Definition: alignment.h:27