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>
119  IntrusivePtr(IntrusivePtr<U> other) noexcept
120  requires(std::is_convertible_v<U*, T*>)
121  : ptr_(other.release()) {
122  // nop
123  }
124 
125  ~IntrusivePtr() {
126  if ( ptr_ )
127  Unref(ptr_);
128  }
129 
130  void swap(IntrusivePtr& other) noexcept { std::swap(ptr_, other.ptr_); }
131 
132  friend void swap(IntrusivePtr& a, IntrusivePtr& b) noexcept {
133  using std::swap;
134  swap(a.ptr_, b.ptr_);
135  }
136 
142  pointer release() noexcept { return std::exchange(ptr_, nullptr); }
143 
144  IntrusivePtr& operator=(IntrusivePtr other) noexcept {
145  swap(other);
146  return *this;
147  }
148 
149  pointer get() const noexcept { return ptr_; }
150 
151  pointer operator->() const noexcept { return ptr_; }
152 
153  reference operator*() const noexcept { return *ptr_; }
154 
155  bool operator!() const noexcept { return ! ptr_; }
156 
157  explicit operator bool() const noexcept { return ptr_ != nullptr; }
158 
159 private:
160  pointer ptr_ = nullptr;
161 };
162 
171 template<class T, class... Ts>
173  // Assumes that objects start with a reference count of 1!
174  return {intrusive_ptr::AdoptRef{}, new T(std::forward<Ts>(args)...)};
175 }
176 
183 template<class T, class U>
185  return {intrusive_ptr::AdoptRef{}, static_cast<T*>(p.release())};
186 }
187 
188 // -- comparison to nullptr ----------------------------------------------------
189 
193 template<class T>
194 bool operator==(const IntrusivePtr<T>& x, std::nullptr_t) {
195  return ! x;
196 }
197 
201 template<class T>
202 bool operator==(std::nullptr_t, const IntrusivePtr<T>& x) {
203  return ! x;
204 }
205 
209 template<class T>
210 bool operator!=(const IntrusivePtr<T>& x, std::nullptr_t) {
211  return static_cast<bool>(x);
212 }
213 
217 template<class T>
218 bool operator!=(std::nullptr_t, const IntrusivePtr<T>& x) {
219  return static_cast<bool>(x);
220 }
221 
222 // -- comparison to raw pointer ------------------------------------------------
223 
227 template<class T>
228 bool operator==(const IntrusivePtr<T>& x, const T* y) {
229  return x.get() == y;
230 }
231 
235 template<class T>
236 bool operator==(const T* x, const IntrusivePtr<T>& y) {
237  return x == y.get();
238 }
239 
243 template<class T>
244 bool operator!=(const IntrusivePtr<T>& x, const T* y) {
245  return x.get() != y;
246 }
247 
251 template<class T>
252 bool operator!=(const T* x, const IntrusivePtr<T>& y) {
253  return x != y.get();
254 }
255 
256 // -- comparison to intrusive pointer ------------------------------------------
257 
258 // Using trailing return type and decltype() here removes this function from
259 // overload resolution if the two pointers types are not comparable (SFINAE).
260 
264 template<class T, class U>
265 auto operator==(const IntrusivePtr<T>& x, const IntrusivePtr<U>& y) -> decltype(x.get() == y.get()) {
266  return x.get() == y.get();
267 }
268 
272 template<class T, class U>
273 auto operator!=(const IntrusivePtr<T>& x, const IntrusivePtr<U>& y) -> decltype(x.get() != y.get()) {
274  return x.get() != y.get();
275 }
276 
277 } // 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:142
IntrusivePtr< T > make_intrusive(Ts &&... args)
Definition: intrusive-ptr.h:172
Definition: intrusive-ptr.h:26
Definition: any.h:7
IntrusivePtr< T > cast_intrusive(IntrusivePtr< U > p) noexcept
Definition: intrusive-ptr.h:184
Definition: intrusive-ptr.h:17
Definition: intrusive-ptr.h:23