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/properties.h"
35
36namespace pw {
37
39
40// Forward declarations.
41template <typename T>
42class WeakPtr;
43
44template <multibuf::Property...>
45class BasicMultiBuf;
46
62template <typename T>
63class SharedPtr final : public ::pw::allocator::internal::ManagedPtr<T> {
64 private:
65 using Base = ::pw::allocator::internal::ManagedPtr<T>;
66 using ControlBlock = ::pw::allocator::internal::ControlBlock;
67
68 public:
69 using element_type = typename Base::element_type;
70 using weak_type = WeakPtr<T>;
71
73 ~SharedPtr() { reset(); }
74
79 constexpr SharedPtr() noexcept = default;
80
85 constexpr SharedPtr(std::nullptr_t) noexcept : SharedPtr() {}
86
88 constexpr SharedPtr(const SharedPtr& other) noexcept : SharedPtr() {
89 *this = other;
90 }
91
96 template <typename U,
97 typename = std::enable_if_t<std::is_assignable_v<T*&, U*>>>
98 constexpr SharedPtr(const SharedPtr<U>& other) noexcept : SharedPtr() {
99 *this = other;
100 }
101
107 template <typename U,
108 typename = std::enable_if_t<std::is_assignable_v<T*&, U*>>>
109 SharedPtr(SharedPtr<U>&& other) noexcept : SharedPtr() {
110 *this = std::move(other);
111 }
112
122 SharedPtr(UniquePtr<T>& owned) noexcept;
123
127 constexpr SharedPtr& operator=(const SharedPtr& other) noexcept {
128 operator= <T>(other);
129 return *this;
130 }
131
138 template <typename U,
139 typename = std::enable_if_t<std::is_assignable_v<T*&, U*>>>
140 constexpr SharedPtr& operator=(const SharedPtr<U>& other) noexcept;
141
148 template <typename U,
149 typename = std::enable_if_t<std::is_assignable_v<T*&, U*>>>
150 SharedPtr& operator=(SharedPtr<U>&& other) noexcept;
151
156 SharedPtr& operator=(std::nullptr_t) noexcept;
157
169 template <typename U,
170 typename = std::enable_if_t<std::is_assignable_v<T*&, U*>>>
171 constexpr explicit operator const SharedPtr<U>&() const {
172 return static_cast<const SharedPtr<U>&>(
173 static_cast<const allocator::internal::BaseManagedPtr&>(*this));
174 }
175
176 [[nodiscard]] friend constexpr bool operator==(const SharedPtr& lhs,
177 std::nullptr_t) {
178 return lhs.Equals(nullptr);
179 }
180 [[nodiscard]] friend constexpr bool operator==(std::nullptr_t,
181 const SharedPtr& rhs) {
182 return rhs.Equals(nullptr);
183 }
184 [[nodiscard]] friend constexpr bool operator==(const SharedPtr& lhs,
185 const SharedPtr& rhs) {
186 return lhs.Equals(rhs) && lhs.control_block_ == rhs.control_block_;
187 }
188
189 [[nodiscard]] friend constexpr bool operator!=(const SharedPtr& lhs,
190 std::nullptr_t) {
191 return !lhs.Equals(nullptr);
192 }
193 [[nodiscard]] friend constexpr bool operator!=(std::nullptr_t,
194 const SharedPtr& rhs) {
195 return !rhs.Equals(nullptr);
196 }
197 [[nodiscard]] friend constexpr bool operator!=(const SharedPtr& lhs,
198 const SharedPtr& rhs) {
199 return !(lhs == rhs);
200 }
201
205 size_t size() const {
206 static_assert(std::is_array_v<T>,
207 "size() cannot be called on non-array types");
208 return control_block_ == nullptr ? 0 : control_block_->size();
209 }
210
213 int32_t use_count() const {
214 return control_block_ == nullptr ? 0 : control_block_->num_shared();
215 }
216
222 void reset() noexcept;
223
225 void swap(SharedPtr& other) noexcept;
226
229 template <typename PtrType>
230 bool owner_before(const PtrType& other) const noexcept {
231 return control_block_ < other.control_block();
232 }
233
234 private:
235 using Layout = allocator::Layout;
236
237 static constexpr bool is_unbounded_array_v =
238 allocator::internal::is_unbounded_array_v<T>;
239
240 // Allow construction with to the implementation of `MakeShared`.
241 friend class Allocator;
242
243 // Allow SharedPtr<T> to access SharedPtr<U> and vice versa.
244 template <typename>
245 friend class SharedPtr;
246
247 // Allow WeakPtr<T> to promote to a SharedPtr<T>.
248 template <typename>
249 friend class WeakPtr;
250
251 // Allow GenericMultiBufs to compose SharedPtr<T>.
252 friend class ::pw::multibuf::internal::GenericMultiBuf;
253
254 // Allow MultiBufs to decompose SharedPtr<T>.
255 template <multibuf::Property...>
256 friend class BasicMultiBuf;
257
268 template <typename... Args>
269 static SharedPtr Create(Allocator* allocator, Args&&... args);
270
282 static SharedPtr Create(Allocator* allocator, size_t count, size_t alignment);
283
288 SharedPtr(element_type* value, ControlBlock* control_block)
289 : Base(value), control_block_(control_block) {}
290
291 ControlBlock* control_block() const { return control_block_; }
292
294 template <typename U,
295 typename = std::enable_if_t<std::is_assignable_v<T*&, U*>>>
296 void CopyFrom(const SharedPtr<U>& other);
297
300 void Release();
301
303 template <typename U>
304 constexpr void CheckArrayTypes();
305
306 ControlBlock* control_block_ = nullptr;
307};
308
310
311// Template method implementations.
312
313template <typename T>
315 if constexpr (std::is_array_v<T>) {
316 control_block_ =
317 ControlBlock::Create(owned.deallocator(), owned.get(), owned.size());
318 } else {
319 control_block_ = ControlBlock::Create(owned.deallocator(), owned.get(), 1);
320 }
321 if (control_block_ != nullptr) {
322 Base::CopyFrom(owned);
323 owned.Release();
324 }
325}
326
327template <typename T>
328template <typename... Args>
329SharedPtr<T> SharedPtr<T>::Create(Allocator* allocator, Args&&... args) {
330 static_assert(!std::is_array_v<T>);
331 auto* control_block = ControlBlock::Create(allocator, Layout::Of<T>(), 1);
332 if (control_block == nullptr) {
333 return nullptr;
334 }
335 auto* t = new (control_block->data()) T(std::forward<Args>(args)...);
336 return SharedPtr<T>(t, control_block);
337}
338
339template <typename T>
340SharedPtr<T> SharedPtr<T>::Create(Allocator* allocator,
341 size_t count,
342 size_t alignment) {
343 static_assert(allocator::internal::is_unbounded_array_v<T>);
344 Layout layout = Layout::Of<T>(count).Align(alignment);
345 auto* control_block = ControlBlock::Create(allocator, layout, count);
346 if (control_block == nullptr) {
347 return nullptr;
348 }
349 auto* t = new (control_block->data()) std::remove_extent_t<T>[count];
350 return SharedPtr<T>(t, control_block);
351}
352
353template <typename T>
354template <typename U, typename>
355constexpr SharedPtr<T>& SharedPtr<T>::operator=(
356 const SharedPtr<U>& other) noexcept {
357 CheckArrayTypes<U>();
358 CopyFrom(other);
359 if (control_block_ != nullptr) {
360 control_block_->IncrementShared();
361 }
362 return *this;
363}
364
365template <typename T>
366template <typename U, typename>
367SharedPtr<T>& SharedPtr<T>::operator=(SharedPtr<U>&& other) noexcept {
368 CheckArrayTypes<U>();
369 reset();
370 CopyFrom(other);
371 other.Release();
372 return *this;
373}
374
375template <typename T>
376SharedPtr<T>& SharedPtr<T>::operator=(std::nullptr_t) noexcept {
377 reset();
378 return *this;
379}
380
381template <typename T>
382void SharedPtr<T>::reset() noexcept {
383 if (*this == nullptr) {
384 return;
385 }
386 auto action = control_block_->DecrementShared();
387 if (action == ControlBlock::Action::kNone) {
388 // Other `SharedPtr`s associated with this control block remain.
389 Release();
390 return;
391 }
392
393 // This was the last `SharedPtr` associated with this control block.
394 Allocator* allocator = control_block_->allocator();
395 if (!Base::HasCapability(allocator, allocator::kSkipsDestroy)) {
396 if constexpr (std::is_array_v<T>) {
397 Base::Destroy(control_block_->size());
398 } else {
399 Base::Destroy();
400 }
401 }
402
403 if (action == ControlBlock::Action::kExpire) {
404 // Keep the control block. Trying to promote any of the remaining
405 // `WeakPtr`s will fail.
406 Base::Resize(allocator, control_block_, sizeof(ControlBlock));
407 } else {
408 // No `WeakPtr`s remain, and all of the memory can be freed.
409 std::destroy_at(control_block_);
410 Base::Deallocate(allocator, control_block_);
411 }
412
413 Release();
414}
415
416template <typename T>
417void SharedPtr<T>::swap(SharedPtr<T>& other) noexcept {
418 Base::Swap(other);
419 std::swap(control_block_, other.control_block_);
420}
421
422template <typename T>
423template <typename U, typename>
424void SharedPtr<T>::CopyFrom(const SharedPtr<U>& other) {
425 CheckArrayTypes<U>();
426 Base::CopyFrom(other);
427 control_block_ = other.control_block_;
428}
429
430template <typename T>
431void SharedPtr<T>::Release() {
432 Base::Release();
433 control_block_ = nullptr;
434}
435
436template <typename T>
437template <typename U>
438constexpr void SharedPtr<T>::CheckArrayTypes() {
439 if constexpr (std::is_array_v<T>) {
440 static_assert(std::is_array_v<U>,
441 "non-array type used with SharedPtr<T[]>");
442 } else {
443 static_assert(!std::is_array_v<U>, "array type used with SharedPtr<T>");
444 }
445}
446
447} // namespace pw
448
449// TODO(b/402489948): Remove when portable atomics are provided by `pw_atomic`.
450#endif // PW_ALLOCATOR_HAS_ATOMICS
451
452#endif // PW_ALLOCATOR_PUBLIC_PW_ALLOCATOR_SHARED_PTR_H_
Definition: allocator.h:43
Definition: multibuf_v2.h:185
Definition: shared_ptr.h:63
size_t size() const
Definition: shared_ptr.h:205
void swap(SharedPtr &other) noexcept
Swaps the managed pointer and deallocator of this and another object.
Definition: shared_ptr.h:417
constexpr SharedPtr & operator=(const SharedPtr< U > &other) noexcept
SharedPtr & operator=(SharedPtr< U > &&other) noexcept
SharedPtr(SharedPtr< U > &&other) noexcept
Definition: shared_ptr.h:109
~SharedPtr()
Releases any currently-held value.
Definition: shared_ptr.h:73
constexpr SharedPtr() noexcept=default
constexpr SharedPtr(const SharedPtr &other) noexcept
Copy-constructs a SharedPtr<T> from a SharedPtr<T>.
Definition: shared_ptr.h:88
int32_t use_count() const
Definition: shared_ptr.h:213
constexpr SharedPtr(const SharedPtr< U > &other) noexcept
Definition: shared_ptr.h:98
constexpr SharedPtr & operator=(const SharedPtr &other) noexcept
Definition: shared_ptr.h:127
void reset() noexcept
Definition: shared_ptr.h:382
Definition: unique_ptr.h:43
Definition: weak_ptr.h:37
Definition: layout.h:58
Property
Basic properties of a MultiBuf.
Definition: properties.h:25
The Pigweed namespace.
Definition: alignment.h:27