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