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#ifndef PW_ALLOCATOR_PUBLIC_PW_ALLOCATOR_SHARED_PTR_H_
17#define PW_ALLOCATOR_PUBLIC_PW_ALLOCATOR_SHARED_PTR_H_
18
19#include "pw_allocator/config.h"
20
21// TODO(b/402489948): Remove when portable atomics are provided by `pw_atomic`.
22#if PW_ALLOCATOR_HAS_ATOMICS
23
24#include <cstddef>
25#include <cstdint>
26#include <utility>
27
28#include "pw_allocator/allocator.h"
29#include "pw_allocator/deallocator.h"
30#include "pw_allocator/internal/control_block.h"
31#include "pw_allocator/internal/managed_ptr.h"
32#include "pw_allocator/layout.h"
33#include "pw_allocator/unique_ptr.h"
34
35namespace pw {
36
38
39// Forward declarations.
40template <typename T>
41class SharedPtr;
42
43template <typename To, typename From>
44constexpr SharedPtr<To> static_pointer_cast(const SharedPtr<From>& p) noexcept;
45
46template <typename To, typename From>
47constexpr SharedPtr<To> const_pointer_cast(const SharedPtr<From>& p) noexcept;
48
49template <typename T>
50class WeakPtr;
51
67template <typename T>
68class SharedPtr final : public ::pw::allocator::internal::ManagedPtr<T> {
69 private:
70 using Base = ::pw::allocator::internal::ManagedPtr<T>;
71 using ControlBlock = ::pw::allocator::internal::ControlBlock;
72 using ControlBlockHandle = ::pw::allocator::internal::ControlBlockHandle;
73
74 public:
75 using element_type = typename Base::element_type;
76 using weak_type = WeakPtr<T>;
77
80
91 template <typename... Args>
92 static SharedPtr Create(Allocator* allocator, Args&&... args);
93
105 static SharedPtr Create(Allocator* allocator, size_t count, size_t alignment);
106
111 constexpr SharedPtr() noexcept = default;
112
117 constexpr SharedPtr(element_type* value, ControlBlock* control_block)
118 : Base(value), control_block_(control_block) {}
119
124 constexpr SharedPtr(std::nullptr_t) noexcept : SharedPtr() {}
125
127 constexpr SharedPtr(const SharedPtr& other) noexcept : SharedPtr() {
128 *this = other;
129 }
130
135 template <typename U,
136 typename = std::enable_if_t<std::is_assignable_v<T*&, U*>>>
137 constexpr SharedPtr(const SharedPtr<U>& other) noexcept : SharedPtr() {
138 *this = other;
139 }
140
146 template <typename U,
147 typename = std::enable_if_t<std::is_assignable_v<T*&, U*>>>
148 SharedPtr(SharedPtr<U>&& other) noexcept : SharedPtr() {
149 *this = std::move(other);
150 }
151
161 SharedPtr(UniquePtr<T>& owned) noexcept;
162
166 constexpr SharedPtr& operator=(const SharedPtr& other) noexcept {
167 operator= <T>(other);
168 return *this;
169 }
170
177 template <typename U,
178 typename = std::enable_if_t<std::is_assignable_v<T*&, U*>>>
179 constexpr SharedPtr& operator=(const SharedPtr<U>& other) noexcept;
180
187 template <typename U,
188 typename = std::enable_if_t<std::is_assignable_v<T*&, U*>>>
189 SharedPtr& operator=(SharedPtr<U>&& other) noexcept;
190
195 SharedPtr& operator=(std::nullptr_t) noexcept;
196
197 // Conversions
198
210 template <typename U>
211 constexpr explicit operator SharedPtr<U>() const {
212 return static_pointer_cast<U>(*this);
213 }
214
227 template <typename To, typename From>
229 const SharedPtr<From>& p) noexcept;
230
241 template <typename To, typename From>
243 const SharedPtr<From>& p) noexcept;
244
245 // Comparisons
246
247 [[nodiscard]] friend constexpr bool operator==(const SharedPtr& lhs,
248 std::nullptr_t) {
249 return lhs.Equals(nullptr);
250 }
251 [[nodiscard]] friend constexpr bool operator==(std::nullptr_t,
252 const SharedPtr& rhs) {
253 return rhs.Equals(nullptr);
254 }
255 [[nodiscard]] friend constexpr bool operator==(const SharedPtr& lhs,
256 const SharedPtr& rhs) {
257 return lhs.Equals(rhs) && lhs.control_block_ == rhs.control_block_;
258 }
259
260 [[nodiscard]] friend constexpr bool operator!=(const SharedPtr& lhs,
261 std::nullptr_t) {
262 return !lhs.Equals(nullptr);
263 }
264 [[nodiscard]] friend constexpr bool operator!=(std::nullptr_t,
265 const SharedPtr& rhs) {
266 return !rhs.Equals(nullptr);
267 }
268 [[nodiscard]] friend constexpr bool operator!=(const SharedPtr& lhs,
269 const SharedPtr& rhs) {
270 return !(lhs == rhs);
271 }
272
273 // Accessors
274
278 constexpr size_t size() const {
279 static_assert(std::is_array_v<T>,
280 "size() cannot be called on non-array types");
281 return control_block_ == nullptr
282 ? 0
283 : (control_block_->size() / sizeof(element_type));
284 }
285
288 constexpr int32_t use_count() const {
289 return control_block_ == nullptr ? 0 : control_block_->num_shared();
290 }
291
294 template <typename PtrType>
295 bool owner_before(const PtrType& other) const noexcept {
296 return std::less{}(control_block_, other.control_block_);
297 }
298
299 // Mutators
300
306 void reset() noexcept;
307
309 void swap(SharedPtr& other) noexcept;
310
314 return control_block_ == nullptr ? nullptr : control_block_->allocator();
315 }
316
321 ControlBlock* GetControlBlock(const ControlBlockHandle&) const {
322 return control_block_;
323 }
324
325 private:
326 using Layout = allocator::Layout;
327
328 // Allow other SharedPtrs and WeakPtrs to access control blocks directly.
329 template <typename>
330 friend class SharedPtr;
331
332 template <typename>
333 friend class WeakPtr;
334
336 template <typename U,
337 typename = std::enable_if_t<std::is_assignable_v<T*&, U*>>>
338 void CopyFrom(const SharedPtr<U>& other);
339
342 void Release();
343
345 template <typename U>
346 constexpr void CheckArrayTypes();
347
348 ControlBlock* control_block_ = nullptr;
349};
350
352
353// Template method implementations.
354
355template <typename T>
357 size_t size = sizeof(element_type);
358 if constexpr (std::is_array_v<T>) {
359 size *= owned.size();
360 }
361
362 // Create the shared pointer's control block using the restricted method.
363 const auto& handle = ControlBlockHandle::GetInstance_DO_NOT_USE();
364 control_block_ =
365 ControlBlock::Create(handle, owned.deallocator(), owned.get(), size);
366
367 if (control_block_ != nullptr) {
368 Base::CopyFrom(owned);
369 owned.Release();
370 }
371}
372
373template <typename T>
374template <typename... Args>
375SharedPtr<T> SharedPtr<T>::Create(Allocator* allocator, Args&&... args) {
376 static_assert(!std::is_array_v<T>);
377
378 // Create the shared pointer's control block using the restricted method.
379 const auto& handle = ControlBlockHandle::GetInstance_DO_NOT_USE();
380 auto* control_block =
381 ControlBlock::Create(handle, allocator, Layout::Of<T>());
382
383 if (control_block == nullptr) {
384 return nullptr;
385 }
386 auto* t = new (control_block->data()) T(std::forward<Args>(args)...);
387 return SharedPtr<T>(t, control_block);
388}
389
390template <typename T>
392 size_t count,
393 size_t alignment) {
394 static_assert(allocator::internal::is_unbounded_array_v<T>);
395 Layout layout = Layout::Of<T>(count).Align(alignment);
396
397 // Create the shared pointer's control block using the restricted method.
398 const auto& handle = ControlBlockHandle::GetInstance_DO_NOT_USE();
399 auto* control_block = ControlBlock::Create(handle, allocator, layout);
400
401 if (control_block == nullptr) {
402 return nullptr;
403 }
404 auto* t = new (control_block->data()) element_type[count];
405 return SharedPtr<T>(t, control_block);
406}
407
408template <typename T>
409template <typename U, typename>
411 const SharedPtr<U>& other) noexcept {
412 CheckArrayTypes<U>();
413 reset();
414 CopyFrom(other);
415 if (control_block_ != nullptr) {
416 control_block_->IncrementShared();
417 }
418 return *this;
419}
420
421template <typename T>
422template <typename U, typename>
423SharedPtr<T>& SharedPtr<T>::operator=(SharedPtr<U>&& other) noexcept {
424 CheckArrayTypes<U>();
425 reset();
426 CopyFrom(other);
427 other.Release();
428 return *this;
429}
430
431template <typename T>
432SharedPtr<T>& SharedPtr<T>::operator=(std::nullptr_t) noexcept {
433 reset();
434 return *this;
435}
436
437template <typename To, typename From>
439 using PtrType = typename SharedPtr<To>::element_type*;
440 if (p.control_block_ != nullptr) {
441 p.control_block_->IncrementShared();
442 }
443 return SharedPtr<To>{static_cast<PtrType>(p.get()), p.control_block_};
444}
445
446template <typename To, typename From>
448 using PtrType = typename SharedPtr<To>::element_type*;
449 if (p.control_block_ != nullptr) {
450 p.control_block_->IncrementShared();
451 }
452 return SharedPtr<To>{const_cast<PtrType>(p.get()), p.control_block_};
453}
454
455template <typename T>
456void SharedPtr<T>::reset() noexcept {
457 if (*this == nullptr) {
458 return;
459 }
460 auto action = control_block_->DecrementShared();
461 if (action == ControlBlock::Action::kNone) {
462 // Other `SharedPtr`s associated with this control block remain.
463 Release();
464 return;
465 }
466
467 // This was the last `SharedPtr` associated with this control block.
468 Allocator* allocator = control_block_->allocator();
469 if (!Base::HasCapability(allocator, allocator::kSkipsDestroy)) {
470 if constexpr (std::is_array_v<T>) {
471 Base::Destroy(size());
472 } else {
473 Base::Destroy();
474 }
475 }
476
477 if (action == ControlBlock::Action::kExpire) {
478 // Keep the control block. Trying to promote any of the remaining
479 // `WeakPtr`s will fail.
480 Base::Resize(allocator, control_block_, sizeof(ControlBlock));
481 } else {
482 // No `WeakPtr`s remain, and all of the memory can be freed.
483 std::destroy_at(control_block_);
484 Base::Deallocate(allocator, control_block_);
485 }
486
487 Release();
488}
489
490template <typename T>
491void SharedPtr<T>::swap(SharedPtr<T>& other) noexcept {
492 Base::Swap(other);
493 std::swap(control_block_, other.control_block_);
494}
495
496template <typename T>
497template <typename U, typename>
498void SharedPtr<T>::CopyFrom(const SharedPtr<U>& other) {
499 CheckArrayTypes<U>();
500 Base::CopyFrom(other);
501 control_block_ = other.control_block_;
502}
503
504template <typename T>
505void SharedPtr<T>::Release() {
506 Base::Release();
507 control_block_ = nullptr;
508}
509
510template <typename T>
511template <typename U>
512constexpr void SharedPtr<T>::CheckArrayTypes() {
513 if constexpr (std::is_array_v<T>) {
514 static_assert(std::is_array_v<U>,
515 "non-array type used with SharedPtr<T[]>");
516 } else {
517 static_assert(!std::is_array_v<U>, "array type used with SharedPtr<T>");
518 }
519}
520
521} // namespace pw
522
523// TODO(b/402489948): Remove when portable atomics are provided by `pw_atomic`.
524#endif // PW_ALLOCATOR_HAS_ATOMICS
525
526#endif // PW_ALLOCATOR_PUBLIC_PW_ALLOCATOR_SHARED_PTR_H_
Definition: allocator.h:45
Definition: shared_ptr.h:68
static SharedPtr Create(Allocator *allocator, size_t count, size_t alignment)
Definition: shared_ptr.h:391
bool owner_before(const PtrType &other) const noexcept
Definition: shared_ptr.h:295
SharedPtr(UniquePtr< T > &owned) noexcept
Definition: shared_ptr.h:356
constexpr SharedPtr(std::nullptr_t) noexcept
Definition: shared_ptr.h:124
friend constexpr SharedPtr< To > const_pointer_cast(const SharedPtr< From > &p) noexcept
Definition: shared_ptr.h:447
void swap(SharedPtr &other) noexcept
Swaps the managed pointer and deallocator of this and another object.
Definition: shared_ptr.h:491
constexpr int32_t use_count() const
Definition: shared_ptr.h:288
SharedPtr & operator=(std::nullptr_t) noexcept
Definition: shared_ptr.h:432
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:148
~SharedPtr()
Releases any currently-held value.
Definition: shared_ptr.h:79
constexpr size_t size() const
Definition: shared_ptr.h:278
friend constexpr SharedPtr< To > static_pointer_cast(const SharedPtr< From > &p) noexcept
Definition: shared_ptr.h:438
ControlBlock * GetControlBlock(const ControlBlockHandle &) const
Definition: shared_ptr.h:321
constexpr SharedPtr() noexcept=default
constexpr SharedPtr(const SharedPtr &other) noexcept
Copy-constructs a SharedPtr<T> from a SharedPtr<T>.
Definition: shared_ptr.h:127
Allocator * allocator() const
Definition: shared_ptr.h:313
constexpr SharedPtr(const SharedPtr< U > &other) noexcept
Definition: shared_ptr.h:137
constexpr SharedPtr & operator=(const SharedPtr &other) noexcept
Definition: shared_ptr.h:166
void reset() noexcept
Definition: shared_ptr.h:456
Definition: unique_ptr.h:43
Definition: weak_ptr.h:37
Definition: layout.h:58
constexpr SharedPtr< To > static_pointer_cast(const SharedPtr< From > &p) noexcept
Definition: shared_ptr.h:438
constexpr SharedPtr< To > const_pointer_cast(const SharedPtr< From > &p) noexcept
Definition: shared_ptr.h:447
The Pigweed namespace.
Definition: alignment.h:27