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#include "pw_multibuf/v2/properties.h"
35
36namespace pw {
37
39
40// Forward declarations.
41template <typename T>
42class SharedPtr;
43
44template <typename To, typename From>
45constexpr SharedPtr<To> static_pointer_cast(const SharedPtr<From>& p) noexcept;
46
47template <typename To, typename From>
48constexpr SharedPtr<To> const_pointer_cast(const SharedPtr<From>& p) noexcept;
49
50template <typename T>
51class WeakPtr;
52
53namespace multibuf {
54
55namespace v1_adapter {
56class MultiBuf;
57} // namespace v1_adapter
58
59namespace v2 {
60template <Property...>
61class BasicMultiBuf;
62} // namespace v2
63
64} // namespace multibuf
65
81template <typename T>
82class SharedPtr final : public ::pw::allocator::internal::ManagedPtr<T> {
83 private:
84 using Base = ::pw::allocator::internal::ManagedPtr<T>;
85 using ControlBlock = ::pw::allocator::internal::ControlBlock;
86
87 public:
88 using element_type = typename Base::element_type;
89 using weak_type = WeakPtr<T>;
90
92 ~SharedPtr() { reset(); }
93
104 template <typename... Args>
105 static SharedPtr Create(Allocator* allocator, Args&&... args);
106
118 static SharedPtr Create(Allocator* allocator, size_t count, size_t alignment);
119
124 constexpr SharedPtr() noexcept = default;
125
130 constexpr SharedPtr(element_type* value, ControlBlock* control_block)
131 : Base(value), control_block_(control_block) {}
132
137 constexpr SharedPtr(std::nullptr_t) noexcept : SharedPtr() {}
138
140 constexpr SharedPtr(const SharedPtr& other) noexcept : SharedPtr() {
141 *this = other;
142 }
143
148 template <typename U,
149 typename = std::enable_if_t<std::is_assignable_v<T*&, U*>>>
150 constexpr SharedPtr(const SharedPtr<U>& other) noexcept : SharedPtr() {
151 *this = other;
152 }
153
159 template <typename U,
160 typename = std::enable_if_t<std::is_assignable_v<T*&, U*>>>
161 SharedPtr(SharedPtr<U>&& other) noexcept : SharedPtr() {
162 *this = std::move(other);
163 }
164
174 SharedPtr(UniquePtr<T>& owned) noexcept;
175
179 constexpr SharedPtr& operator=(const SharedPtr& other) noexcept {
180 operator= <T>(other);
181 return *this;
182 }
183
190 template <typename U,
191 typename = std::enable_if_t<std::is_assignable_v<T*&, U*>>>
192 constexpr SharedPtr& operator=(const SharedPtr<U>& other) noexcept;
193
200 template <typename U,
201 typename = std::enable_if_t<std::is_assignable_v<T*&, U*>>>
202 SharedPtr& operator=(SharedPtr<U>&& other) noexcept;
203
208 SharedPtr& operator=(std::nullptr_t) noexcept;
209
210 // Conversions
211
223 template <typename U>
224 constexpr explicit operator SharedPtr<U>() const {
225 return static_pointer_cast<U>(*this);
226 }
227
240 template <typename To, typename From>
242 const SharedPtr<From>& p) noexcept;
243
254 template <typename To, typename From>
256 const SharedPtr<From>& p) noexcept;
257
258 // Comparisons
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.Equals(rhs) && lhs.control_block_ == rhs.control_block_;
271 }
272
273 [[nodiscard]] friend constexpr bool operator!=(const SharedPtr& lhs,
274 std::nullptr_t) {
275 return !lhs.Equals(nullptr);
276 }
277 [[nodiscard]] friend constexpr bool operator!=(std::nullptr_t,
278 const SharedPtr& rhs) {
279 return !rhs.Equals(nullptr);
280 }
281 [[nodiscard]] friend constexpr bool operator!=(const SharedPtr& lhs,
282 const SharedPtr& rhs) {
283 return !(lhs == rhs);
284 }
285
286 // Accessors
287
291 constexpr size_t size() const {
292 static_assert(std::is_array_v<T>,
293 "size() cannot be called on non-array types");
294 return control_block_ == nullptr
295 ? 0
296 : (control_block_->size() / sizeof(element_type));
297 }
298
301 constexpr int32_t use_count() const {
302 return control_block_ == nullptr ? 0 : control_block_->num_shared();
303 }
304
307 template <typename PtrType>
308 bool owner_before(const PtrType& other) const noexcept {
309 return control_block_ < other.control_block();
310 }
311
312 // Mutators
313
319 void reset() noexcept;
320
322 void swap(SharedPtr& other) noexcept;
323
326 Allocator* allocator() const {
327 return control_block_ == nullptr ? nullptr : control_block_->allocator();
328 }
329
330 private:
331 using Layout = allocator::Layout;
332
333 // Allow SharedPtrs, WeakPtrs, and MultiBufs to access control blocks.
334 template <typename>
335 friend class SharedPtr;
336
337 template <typename>
338 friend class WeakPtr;
339
341
342 template <multibuf::v2::Property...>
343 friend class multibuf::v2::BasicMultiBuf;
344
345 ControlBlock* control_block() const { return control_block_; }
346
348 template <typename U,
349 typename = std::enable_if_t<std::is_assignable_v<T*&, U*>>>
350 void CopyFrom(const SharedPtr<U>& other);
351
354 void Release();
355
357 template <typename U>
358 constexpr void CheckArrayTypes();
359
360 ControlBlock* control_block_ = nullptr;
361};
362
364
365// Template method implementations.
366
367template <typename T>
369 size_t size = sizeof(element_type);
370 if constexpr (std::is_array_v<T>) {
371 size *= owned.size();
372 }
373 control_block_ = ControlBlock::Create(owned.deallocator(), owned.get(), size);
374 if (control_block_ != nullptr) {
375 Base::CopyFrom(owned);
376 owned.Release();
377 }
378}
379
380template <typename T>
381template <typename... Args>
382SharedPtr<T> SharedPtr<T>::Create(Allocator* allocator, Args&&... args) {
383 static_assert(!std::is_array_v<T>);
384 auto* control_block = ControlBlock::Create(allocator, Layout::Of<T>());
385 if (control_block == nullptr) {
386 return nullptr;
387 }
388 auto* t = new (control_block->data()) T(std::forward<Args>(args)...);
389 return SharedPtr<T>(t, control_block);
390}
391
392template <typename T>
394 size_t count,
395 size_t alignment) {
396 static_assert(allocator::internal::is_unbounded_array_v<T>);
397 Layout layout = Layout::Of<T>(count).Align(alignment);
398 auto* control_block = ControlBlock::Create(allocator, layout);
399 if (control_block == nullptr) {
400 return nullptr;
401 }
402 auto* t = new (control_block->data()) element_type[count];
403 return SharedPtr<T>(t, control_block);
404}
405
406template <typename T>
407template <typename U, typename>
409 const SharedPtr<U>& other) noexcept {
410 CheckArrayTypes<U>();
411 reset();
412 CopyFrom(other);
413 if (control_block_ != nullptr) {
414 control_block_->IncrementShared();
415 }
416 return *this;
417}
418
419template <typename T>
420template <typename U, typename>
421SharedPtr<T>& SharedPtr<T>::operator=(SharedPtr<U>&& other) noexcept {
422 CheckArrayTypes<U>();
423 reset();
424 CopyFrom(other);
425 other.Release();
426 return *this;
427}
428
429template <typename T>
430SharedPtr<T>& SharedPtr<T>::operator=(std::nullptr_t) noexcept {
431 reset();
432 return *this;
433}
434
435template <typename To, typename From>
437 using PtrType = typename SharedPtr<To>::element_type*;
438 if (p.control_block_ != nullptr) {
439 p.control_block_->IncrementShared();
440 }
441 return SharedPtr<To>{static_cast<PtrType>(p.get()), p.control_block_};
442}
443
444template <typename To, typename From>
446 using PtrType = typename SharedPtr<To>::element_type*;
447 if (p.control_block_ != nullptr) {
448 p.control_block_->IncrementShared();
449 }
450 return SharedPtr<To>{const_cast<PtrType>(p.get()), p.control_block_};
451}
452
453template <typename T>
454void SharedPtr<T>::reset() noexcept {
455 if (*this == nullptr) {
456 return;
457 }
458 auto action = control_block_->DecrementShared();
459 if (action == ControlBlock::Action::kNone) {
460 // Other `SharedPtr`s associated with this control block remain.
461 Release();
462 return;
463 }
464
465 // This was the last `SharedPtr` associated with this control block.
466 Allocator* allocator = control_block_->allocator();
467 if (!Base::HasCapability(allocator, allocator::kSkipsDestroy)) {
468 if constexpr (std::is_array_v<T>) {
469 Base::Destroy(size());
470 } else {
471 Base::Destroy();
472 }
473 }
474
475 if (action == ControlBlock::Action::kExpire) {
476 // Keep the control block. Trying to promote any of the remaining
477 // `WeakPtr`s will fail.
478 Base::Resize(allocator, control_block_, sizeof(ControlBlock));
479 } else {
480 // No `WeakPtr`s remain, and all of the memory can be freed.
481 std::destroy_at(control_block_);
482 Base::Deallocate(allocator, control_block_);
483 }
484
485 Release();
486}
487
488template <typename T>
489void SharedPtr<T>::swap(SharedPtr<T>& other) noexcept {
490 Base::Swap(other);
491 std::swap(control_block_, other.control_block_);
492}
493
494template <typename T>
495template <typename U, typename>
496void SharedPtr<T>::CopyFrom(const SharedPtr<U>& other) {
497 CheckArrayTypes<U>();
498 Base::CopyFrom(other);
499 control_block_ = other.control_block_;
500}
501
502template <typename T>
503void SharedPtr<T>::Release() {
504 Base::Release();
505 control_block_ = nullptr;
506}
507
508template <typename T>
509template <typename U>
510constexpr void SharedPtr<T>::CheckArrayTypes() {
511 if constexpr (std::is_array_v<T>) {
512 static_assert(std::is_array_v<U>,
513 "non-array type used with SharedPtr<T[]>");
514 } else {
515 static_assert(!std::is_array_v<U>, "array type used with SharedPtr<T>");
516 }
517}
518
519} // namespace pw
520
521// TODO(b/402489948): Remove when portable atomics are provided by `pw_atomic`.
522#endif // PW_ALLOCATOR_HAS_ATOMICS
523
524#endif // PW_ALLOCATOR_PUBLIC_PW_ALLOCATOR_SHARED_PTR_H_
Definition: allocator.h:45
Definition: shared_ptr.h:82
static SharedPtr Create(Allocator *allocator, size_t count, size_t alignment)
Definition: shared_ptr.h:393
bool owner_before(const PtrType &other) const noexcept
Definition: shared_ptr.h:308
SharedPtr(UniquePtr< T > &owned) noexcept
Definition: shared_ptr.h:368
constexpr SharedPtr(std::nullptr_t) noexcept
Definition: shared_ptr.h:137
friend constexpr SharedPtr< To > const_pointer_cast(const SharedPtr< From > &p) noexcept
Definition: shared_ptr.h:445
void swap(SharedPtr &other) noexcept
Swaps the managed pointer and deallocator of this and another object.
Definition: shared_ptr.h:489
constexpr int32_t use_count() const
Definition: shared_ptr.h:301
SharedPtr & operator=(std::nullptr_t) noexcept
Definition: shared_ptr.h:430
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:161
~SharedPtr()
Releases any currently-held value.
Definition: shared_ptr.h:92
constexpr size_t size() const
Definition: shared_ptr.h:291
friend constexpr SharedPtr< To > static_pointer_cast(const SharedPtr< From > &p) noexcept
Definition: shared_ptr.h:436
constexpr SharedPtr() noexcept=default
constexpr SharedPtr(const SharedPtr &other) noexcept
Copy-constructs a SharedPtr<T> from a SharedPtr<T>.
Definition: shared_ptr.h:140
constexpr SharedPtr(const SharedPtr< U > &other) noexcept
Definition: shared_ptr.h:150
constexpr SharedPtr & operator=(const SharedPtr &other) noexcept
Definition: shared_ptr.h:179
void reset() noexcept
Definition: shared_ptr.h:454
Definition: unique_ptr.h:43
Definition: weak_ptr.h:37
Definition: layout.h:58
Definition: multibuf.h:184
Definition: multibuf.h:194
constexpr SharedPtr< To > static_pointer_cast(const SharedPtr< From > &p) noexcept
Definition: shared_ptr.h:436
constexpr SharedPtr< To > const_pointer_cast(const SharedPtr< From > &p) noexcept
Definition: shared_ptr.h:445
Property
Basic properties of a MultiBuf.
Definition: properties.h:24
BasicMultiBuf< Property::kLayerable > MultiBuf
Definition: multibuf.h:62
The Pigweed namespace.
Definition: alignment.h:27