libcopp 2.3.1
All Data Structures Namespaces Files Functions Variables Typedefs Enumerations Enumerator Friends Macros Pages
intrusive_ptr.h
Go to the documentation of this file.
1// Copyright 2025 owent
2// Created by owent on 2017-05-18
3
4#pragma once
5
6// clang-format off
7#include <libcopp/utils/config/stl_include_prefix.h> // NOLINT(build/include_order)
8// clang-format on
9#include <assert.h>
10#include <cstddef>
11#include <memory>
12#include <ostream>
13
14#ifdef __cpp_impl_three_way_comparison
15# include <compare>
16#endif
17// clang-format off
18#include <libcopp/utils/config/stl_include_suffix.h> // NOLINT(build/include_order)
19// clang-format on
20
22#include <libcopp/utils/config/libcopp_build_features.h>
23
24LIBCOPP_COPP_NAMESPACE_BEGIN
25namespace memory {
26//
27// intrusive_ptr
28//
29// A smart pointer that uses intrusive reference counting.
30//
31// Relies on unqualified calls to
32//
33// void intrusive_ptr_add_ref(T * p);
34// void intrusive_ptr_release(T * p);
35//
36// (p != nullptr)
37//
38// The object is responsible for destroying itself.
39//
40
41template <typename T>
43 public:
45 using element_type = T;
46
47 constexpr intrusive_ptr() noexcept : px(nullptr) {}
48
49 intrusive_ptr(T *p, bool add_ref = true) : px(p) {
50 if (px != nullptr && add_ref) {
51 intrusive_ptr_add_ref(px);
52 }
53 }
54
55 template <typename U>
57 typename std::enable_if<std::is_convertible<U *, T *>::value>::type * = nullptr)
58 : px(rhs.get()) {
59 if (px != nullptr) {
60 intrusive_ptr_add_ref(px);
61 }
62 }
63
64 intrusive_ptr(self_type const &rhs) : px(rhs.px) {
65 if (px != nullptr) {
66 intrusive_ptr_add_ref(px);
67 }
68 }
69
71 if (px != nullptr) {
72 intrusive_ptr_release(px);
73 }
74 }
75
76 template <typename U>
77 friend class intrusive_ptr;
78
79 template <typename U>
81 self_type(rhs).swap(*this);
82 return *this;
83 }
84
85 // Move support
86 intrusive_ptr(self_type &&rhs) noexcept : px(rhs.px) { rhs.px = nullptr; }
87
88 self_type &operator=(self_type &&rhs) noexcept {
89 self_type(static_cast<self_type &&>(rhs)).swap(*this);
90 return *this;
91 }
92
93 template <typename U>
95 typename std::enable_if<std::is_convertible<U *, T *>::value>::type * = nullptr) noexcept
96 : px(rhs.px) {
97 rhs.px = nullptr;
98 }
99
100 template <typename U, typename Deleter>
101 self_type &operator=(std::unique_ptr<U, Deleter> &&rhs) {
102 self_type(rhs.release()).swap(*this);
103 return *this;
104 }
105
107 self_type(rhs).swap(*this);
108 return *this;
109 }
110
111 inline void reset() noexcept { self_type().swap(*this); }
112
113 inline void reset(element_type *rhs) { self_type(rhs).swap(*this); }
114
115 inline void reset(element_type *rhs, bool add_ref) { self_type(rhs, add_ref).swap(*this); }
116
117 inline element_type *get() const noexcept { return px; }
118
119 inline element_type *detach() noexcept {
120 element_type *ret = px;
121 px = nullptr;
122 return ret;
123 }
124
125 inline element_type &operator*() const {
126 assert(px != 0);
127 return *px;
128 }
129
130 inline element_type *operator->() const {
131 assert(px != 0);
132 return px;
133 }
134
135 // implicit conversion to "bool"
136 inline operator bool() const noexcept { return px != nullptr; }
137 // operator! is redundant, but some compilers need it
138 inline bool operator!() const noexcept { return px == nullptr; }
139
140 inline void swap(intrusive_ptr &rhs) noexcept {
141 element_type *tmp = px;
142 px = rhs.px;
143 rhs.px = tmp;
144 }
145
146 private:
148};
149
150template <typename T, typename U>
151inline bool operator==(intrusive_ptr<T> const &a, intrusive_ptr<U> const &b) noexcept {
152 return a.get() == b.get();
153}
154
155template <typename T, typename U>
156inline bool operator==(intrusive_ptr<T> const &a, U *b) noexcept {
157 return a.get() == b;
158}
159
160template <typename T, typename U>
161inline bool operator==(T *a, intrusive_ptr<U> const &b) noexcept {
162 return a == b.get();
163}
164
165#ifdef __cpp_impl_three_way_comparison
166template <typename T, typename U>
167inline std::strong_ordering operator<=>(intrusive_ptr<T> const &a, intrusive_ptr<U> const &b) noexcept {
168 return a.get() <=> b.get();
169}
170
171template <typename T, typename U>
172inline std::strong_ordering operator<=>(intrusive_ptr<T> const &a, U *b) noexcept {
173 return a.get() <=> b;
174}
175
176template <typename T, typename U>
177inline std::strong_ordering operator<=>(T *a, intrusive_ptr<U> const &b) noexcept {
178 return a <=> b.get();
179}
180#else
181template <typename T, typename U>
182inline bool operator!=(intrusive_ptr<T> const &a, intrusive_ptr<U> const &b) noexcept {
183 return a.get() != b.get();
184}
185
186template <typename T, typename U>
187inline bool operator!=(intrusive_ptr<T> const &a, U *b) noexcept {
188 return a.get() != b;
189}
190
191template <typename T, typename U>
192inline bool operator!=(T *a, intrusive_ptr<U> const &b) noexcept {
193 return a != b.get();
194}
195
196template <typename T, typename U>
197inline bool operator<(intrusive_ptr<T> const &a, intrusive_ptr<U> const &b) noexcept {
198 return a.get() < b.get();
199}
200
201template <typename T, typename U>
202inline bool operator<(intrusive_ptr<T> const &a, U *b) noexcept {
203 return a.get() < b;
204}
205
206template <typename T, typename U>
207inline bool operator<(T *a, intrusive_ptr<U> const &b) noexcept {
208 return a < b.get();
209}
210
211template <typename T, typename U>
212inline bool operator<=(intrusive_ptr<T> const &a, intrusive_ptr<U> const &b) noexcept {
213 return a.get() <= b.get();
214}
215
216template <typename T, typename U>
217inline bool operator<=(intrusive_ptr<T> const &a, U *b) noexcept {
218 return a.get() <= b;
219}
220
221template <typename T, typename U>
222inline bool operator<=(T *a, intrusive_ptr<U> const &b) noexcept {
223 return a <= b.get();
224}
225
226template <typename T, typename U>
227inline bool operator>(intrusive_ptr<T> const &a, intrusive_ptr<U> const &b) noexcept {
228 return a.get() > b.get();
229}
230
231template <typename T, typename U>
232inline bool operator>(intrusive_ptr<T> const &a, U *b) noexcept {
233 return a.get() > b;
234}
235
236template <typename T, typename U>
237inline bool operator>(T *a, intrusive_ptr<U> const &b) noexcept {
238 return a > b.get();
239}
240
241template <typename T, typename U>
242inline bool operator>=(intrusive_ptr<T> const &a, intrusive_ptr<U> const &b) noexcept {
243 return a.get() >= b.get();
244}
245
246template <typename T, typename U>
247inline bool operator>=(intrusive_ptr<T> const &a, U *b) noexcept {
248 return a.get() >= b;
249}
250
251template <typename T, typename U>
252inline bool operator>=(T *a, intrusive_ptr<U> const &b) noexcept {
253 return a >= b.get();
254}
255
256#endif
257
258template <typename T>
259inline bool operator==(intrusive_ptr<T> const &p, std::nullptr_t) noexcept {
260 return p.get() == nullptr;
261}
262
263template <typename T>
264inline bool operator==(std::nullptr_t, intrusive_ptr<T> const &p) noexcept {
265 return p.get() == nullptr;
266}
267
268#ifdef __cpp_impl_three_way_comparison
269template <typename T>
270inline std::strong_ordering operator<=>(intrusive_ptr<T> const &p, std::nullptr_t) noexcept {
271 return p.get() <=> nullptr;
272}
273
274template <typename T>
275inline std::strong_ordering operator<=>(std::nullptr_t, intrusive_ptr<T> const &p) noexcept {
276 return p.get() <=> nullptr;
277}
278#else
279template <typename T>
280inline bool operator!=(intrusive_ptr<T> const &p, std::nullptr_t) noexcept {
281 return p.get() != nullptr;
282}
283
284template <typename T>
285inline bool operator!=(std::nullptr_t, intrusive_ptr<T> const &p) noexcept {
286 return p.get() != nullptr;
287}
288
289template <typename T>
290inline bool operator<(intrusive_ptr<T> const &p, std::nullptr_t) noexcept {
291 return p.get() < nullptr;
292}
293
294template <typename T>
295inline bool operator<(std::nullptr_t, intrusive_ptr<T> const &p) noexcept {
296 return p.get() < nullptr;
297}
298
299template <typename T>
300inline bool operator<=(intrusive_ptr<T> const &p, std::nullptr_t) noexcept {
301 return p.get() <= nullptr;
302}
303
304template <typename T>
305inline bool operator<=(std::nullptr_t, intrusive_ptr<T> const &p) noexcept {
306 return p.get() <= nullptr;
307}
308
309template <typename T>
310inline bool operator>(intrusive_ptr<T> const &p, std::nullptr_t) noexcept {
311 return p.get() > nullptr;
312}
313
314template <typename T>
315inline bool operator>(std::nullptr_t, intrusive_ptr<T> const &p) noexcept {
316 return p.get() > nullptr;
317}
318
319template <typename T>
320inline bool operator>=(intrusive_ptr<T> const &p, std::nullptr_t) noexcept {
321 return p.get() >= nullptr;
322}
323
324template <typename T>
325inline bool operator>=(std::nullptr_t, intrusive_ptr<T> const &p) noexcept {
326 return p.get() >= nullptr;
327}
328#endif
329
330template <typename T>
332 lhs.swap(rhs);
333}
334
335// mem_fn support
336
337template <typename T>
339 return p.get();
340}
341
342template <typename T, typename U>
344 return static_cast<T *>(p.get());
345}
346
347template <typename T, typename U>
349 return const_cast<T *>(p.get());
350}
351
352#if defined(LIBCOPP_MACRO_ENABLE_RTTI) && LIBCOPP_MACRO_ENABLE_RTTI
353template <typename T, typename U>
354intrusive_ptr<T> dynamic_pointer_cast(intrusive_ptr<U> const &p) {
355 return dynamic_cast<T *>(p.get());
356}
357#endif
358
359// operator<<
360template <typename E, typename T, typename Y>
361std::basic_ostream<E, T> &operator<<(std::basic_ostream<E, T> &os, intrusive_ptr<Y> const &p) {
362 os << p.get();
363 return os;
364}
365} // namespace memory
366LIBCOPP_COPP_NAMESPACE_END
367
368#if LIBCOPP_MACRO_ENABLE_MULTI_THREAD
369# define LIBCOPP_UTIL_INTRUSIVE_PTR_ATOMIC_TYPE LIBCOPP_COPP_NAMESPACE_ID::util::lock::atomic_int_type<size_t>
370#else
371# define LIBCOPP_UTIL_INTRUSIVE_PTR_ATOMIC_TYPE \
372 LIBCOPP_COPP_NAMESPACE_ID::util::lock::atomic_int_type< \
373 LIBCOPP_COPP_NAMESPACE_ID::util::lock::unsafe_int_type<size_t> >
374#endif
375
376#define LIBCOPP_UTIL_INTRUSIVE_PTR_REF_MEMBER_DECL(T) \
377 \
378 private: \
379 LIBCOPP_UTIL_INTRUSIVE_PTR_ATOMIC_TYPE intrusive_ref_counter_; \
380 friend void intrusive_ptr_add_ref(T *p); \
381 friend void intrusive_ptr_release(T *p); \
382 \
383 public: \
384 const size_t use_count() const { return intrusive_ref_counter_.load(); }
385
386#define LIBCOPP_UTIL_INTRUSIVE_PTR_REF_FN_DECL(T) \
387 void intrusive_ptr_add_ref(T *p); \
388 void intrusive_ptr_release(T *p);
389
390#define LIBCOPP_UTIL_INTRUSIVE_PTR_REF_MEMBER_INIT() this->intrusive_ref_counter_.store(0)
391
392#define LIBCOPP_UTIL_INTRUSIVE_PTR_REF_FN_DEFI(T) \
393 void intrusive_ptr_add_ref(T *p) { \
394 if (nullptr != p) { \
395 ++p->intrusive_ref_counter_; \
396 } \
397 } \
398 void intrusive_ptr_release(T *p) { \
399 if (nullptr == p) { \
400 return; \
401 } \
402 assert(p->intrusive_ref_counter_.load() > 0); \
403 size_t ref = --p->intrusive_ref_counter_; \
404 if (0 == ref) { \
405 delete p; \
406 } \
407 }
constexpr intrusive_ptr() noexcept
intrusive_ptr(self_type const &rhs)
intrusive_ptr & operator=(intrusive_ptr< U > const &rhs)
element_type * get() const noexcept
bool operator!() const noexcept
self_type & operator=(self_type &&rhs) noexcept
intrusive_ptr(T *p, bool add_ref=true)
element_type * operator->() const
intrusive_ptr< T > self_type
intrusive_ptr(intrusive_ptr< U > &&rhs, typename std::enable_if< std::is_convertible< U *, T * >::value >::type *=nullptr) noexcept
element_type & operator*() const
void swap(intrusive_ptr &rhs) noexcept
self_type & operator=(std::unique_ptr< U, Deleter > &&rhs)
intrusive_ptr(intrusive_ptr< U > const &rhs, typename std::enable_if< std::is_convertible< U *, T * >::value >::type *=nullptr)
void reset(element_type *rhs, bool add_ref)
intrusive_ptr(self_type &&rhs) noexcept
element_type * detach() noexcept
self_type & operator=(self_type const &rhs)
void reset(element_type *rhs)
void reset() noexcept
T * get_pointer(intrusive_ptr< T > const &p)
intrusive_ptr< T > const_pointer_cast(intrusive_ptr< U > const &p)
bool operator==(intrusive_ptr< T > const &a, intrusive_ptr< U > const &b) noexcept
bool operator<(intrusive_ptr< T > const &a, intrusive_ptr< U > const &b) noexcept
std::basic_ostream< E, T > & operator<<(std::basic_ostream< E, T > &os, intrusive_ptr< Y > const &p)
bool operator>(intrusive_ptr< T > const &a, intrusive_ptr< U > const &b) noexcept
intrusive_ptr< T > static_pointer_cast(intrusive_ptr< U > const &p)
bool operator<=(intrusive_ptr< T > const &a, intrusive_ptr< U > const &b) noexcept
void swap(intrusive_ptr< T > &lhs, intrusive_ptr< T > &rhs)
bool operator!=(intrusive_ptr< T > const &a, intrusive_ptr< U > const &b) noexcept
bool operator>=(intrusive_ptr< T > const &a, intrusive_ptr< U > const &b) noexcept