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