Spicy
intrusive-ptr.h
1 // Copyright (c) 2020-now by the Zeek Project. See LICENSE for details.
2 
3 #pragma once
4 
5 #include <cstdint>
6 #include <type_traits>
7 #include <utility>
8 
9 namespace hilti::rt {
10 
11 namespace intrusive_ptr {
12 
17 struct AdoptRef {};
18 
23 struct NewRef {};
24 
27 private:
28  template<typename T>
29  friend void Ref(const T*);
30  template<typename T>
31  friend void Unref(const T*);
32  mutable uint64_t _references = 1;
33 };
34 
35 template<typename T>
36 inline void Ref(const T* m) {
37  if ( m )
38  ++m->_references;
39 }
40 
41 template<typename T>
42 inline void Unref(const T* m) {
43  if ( m && --m->_references == 0 )
44  delete m;
45 }
46 
47 } // namespace intrusive_ptr
48 
66 template<class T>
67 class IntrusivePtr {
68 public:
69  // -- member types
70 
71  using pointer = T*;
72 
73  using const_pointer = const T*;
74 
75  using element_type = T;
76 
77  using reference = T&;
78 
79  using const_reference = const T&;
80 
81  // -- constructors, destructors, and assignment operators
82 
83  constexpr IntrusivePtr() noexcept = default;
84 
85  constexpr IntrusivePtr(std::nullptr_t) noexcept : IntrusivePtr() {
86  // nop
87  }
88 
97  constexpr IntrusivePtr(intrusive_ptr::AdoptRef, pointer raw_ptr) noexcept : ptr_(raw_ptr) {}
98 
107  IntrusivePtr(intrusive_ptr::NewRef, pointer raw_ptr) noexcept : ptr_(raw_ptr) {
108  if ( ptr_ )
109  Ref(ptr_);
110  }
111 
112  IntrusivePtr(IntrusivePtr&& other) noexcept : ptr_(other.release()) {
113  // nop
114  }
115 
116  IntrusivePtr(const IntrusivePtr& other) noexcept : IntrusivePtr(intrusive_ptr::NewRef{}, other.get()) {}
117 
118  template<class U, class = std::enable_if_t<std::is_convertible_v<U*, T*>>>
119  IntrusivePtr(IntrusivePtr<U> other) noexcept : ptr_(other.release()) {
120  // nop
121  }
122 
123  ~IntrusivePtr() {
124  if ( ptr_ )
125  Unref(ptr_);
126  }
127 
128  void swap(IntrusivePtr& other) noexcept { std::swap(ptr_, other.ptr_); }
129 
130  friend void swap(IntrusivePtr& a, IntrusivePtr& b) noexcept {
131  using std::swap;
132  swap(a.ptr_, b.ptr_);
133  }
134 
140  pointer release() noexcept { return std::exchange(ptr_, nullptr); }
141 
142  IntrusivePtr& operator=(IntrusivePtr other) noexcept {
143  swap(other);
144  return *this;
145  }
146 
147  pointer get() const noexcept {
148  // Some versions of GCC diagnose a maybe uninitialized variable here.
149  // Since we always initialize the field this should not be possible.
150 #pragma GCC diagnostic push
151 #pragma GCC diagnostic ignored "-Wpragmas"
152 #pragma GCC diagnostic ignored "-Wunknown-warning-option"
153 #pragma GCC diagnostic ignored "-Wmaybe-uninitialized"
154  return ptr_;
155 #pragma GCC diagnostic pop
156  }
157 
158  pointer operator->() const noexcept { return ptr_; }
159 
160  reference operator*() const noexcept { return *ptr_; }
161 
162  bool operator!() const noexcept { return ! ptr_; }
163 
164  explicit operator bool() const noexcept { return ptr_ != nullptr; }
165 
166 private:
167  pointer ptr_ = nullptr;
168 };
169 
178 template<class T, class... Ts>
180  // Assumes that objects start with a reference count of 1!
181  return {intrusive_ptr::AdoptRef{}, new T(std::forward<Ts>(args)...)};
182 }
183 
190 template<class T, class U>
192  return {intrusive_ptr::AdoptRef{}, static_cast<T*>(p.release())};
193 }
194 
195 // -- comparison to nullptr ----------------------------------------------------
196 
200 template<class T>
201 bool operator==(const IntrusivePtr<T>& x, std::nullptr_t) {
202  return ! x;
203 }
204 
208 template<class T>
209 bool operator==(std::nullptr_t, const IntrusivePtr<T>& x) {
210  return ! x;
211 }
212 
216 template<class T>
217 bool operator!=(const IntrusivePtr<T>& x, std::nullptr_t) {
218  return static_cast<bool>(x);
219 }
220 
224 template<class T>
225 bool operator!=(std::nullptr_t, const IntrusivePtr<T>& x) {
226  return static_cast<bool>(x);
227 }
228 
229 // -- comparison to raw pointer ------------------------------------------------
230 
234 template<class T>
235 bool operator==(const IntrusivePtr<T>& x, const T* y) {
236  return x.get() == y;
237 }
238 
242 template<class T>
243 bool operator==(const T* x, const IntrusivePtr<T>& y) {
244  return x == y.get();
245 }
246 
250 template<class T>
251 bool operator!=(const IntrusivePtr<T>& x, const T* y) {
252  return x.get() != y;
253 }
254 
258 template<class T>
259 bool operator!=(const T* x, const IntrusivePtr<T>& y) {
260  return x != y.get();
261 }
262 
263 // -- comparison to intrusive pointer ------------------------------------------
264 
265 // Using trailing return type and decltype() here removes this function from
266 // overload resolution if the two pointers types are not comparable (SFINAE).
267 
271 template<class T, class U>
272 auto operator==(const IntrusivePtr<T>& x, const IntrusivePtr<U>& y) -> decltype(x.get() == y.get()) {
273  return x.get() == y.get();
274 }
275 
279 template<class T, class U>
280 auto operator!=(const IntrusivePtr<T>& x, const IntrusivePtr<U>& y) -> decltype(x.get() != y.get()) {
281  return x.get() != y.get();
282 }
283 
284 } // namespace hilti::rt
Definition: intrusive-ptr.h:67
IntrusivePtr(intrusive_ptr::NewRef, pointer raw_ptr) noexcept
Definition: intrusive-ptr.h:107
constexpr IntrusivePtr(intrusive_ptr::AdoptRef, pointer raw_ptr) noexcept
Definition: intrusive-ptr.h:97
pointer release() noexcept
Definition: intrusive-ptr.h:140
IntrusivePtr< T > make_intrusive(Ts &&... args)
Definition: intrusive-ptr.h:179
Definition: intrusive-ptr.h:26
Definition: any.h:7
IntrusivePtr< T > cast_intrusive(IntrusivePtr< U > p) noexcept
Definition: intrusive-ptr.h:191
Definition: intrusive-ptr.h:17
Definition: intrusive-ptr.h:23