Spicy
intrusive-ptr.h
1 // See the file "COPYING" in the main distribution directory for copyright.
2 //
3 // This code is adapted from Zeek's IntrusivePtr implementation.
4 
5 #pragma once
6 
7 #include <iostream>
8 #include <type_traits>
9 #include <utility>
10 
11 namespace hilti::rt {
12 
13 namespace intrusive_ptr {
14 
19 struct AdoptRef {};
20 
25 struct NewRef {};
26 
29 private:
30  template<typename T>
31  friend void Ref(const T*);
32  template<typename T>
33  friend void Unref(const T*);
34  mutable uint64_t _references = 1;
35 };
36 
37 template<typename T>
38 inline void Ref(const T* m) {
39  if ( m )
40  ++m->_references;
41 }
42 
43 template<typename T>
44 inline void Unref(const T* m) {
45  if ( m && --m->_references == 0 )
46  delete m;
47 }
48 
49 } // namespace intrusive_ptr
50 
68 template<class T>
69 class IntrusivePtr {
70 public:
71  // -- member types
72 
73  using pointer = T*;
74 
75  using const_pointer = const T*;
76 
77  using element_type = T;
78 
79  using reference = T&;
80 
81  using const_reference = const T&;
82 
83  // -- constructors, destructors, and assignment operators
84 
85  constexpr IntrusivePtr() noexcept = default;
86 
87  constexpr IntrusivePtr(std::nullptr_t) noexcept : IntrusivePtr() {
88  // nop
89  }
90 
99  constexpr IntrusivePtr(intrusive_ptr::AdoptRef, pointer raw_ptr) noexcept : ptr_(raw_ptr) {}
100 
109  IntrusivePtr(intrusive_ptr::NewRef, pointer raw_ptr) noexcept : ptr_(raw_ptr) {
110  if ( ptr_ )
111  Ref(ptr_);
112  }
113 
114  IntrusivePtr(IntrusivePtr&& other) noexcept : ptr_(other.release()) {
115  // nop
116  }
117 
118  IntrusivePtr(const IntrusivePtr& other) noexcept : IntrusivePtr(intrusive_ptr::NewRef{}, other.get()) {}
119 
120  template<class U, class = std::enable_if_t<std::is_convertible_v<U*, T*>>>
121  IntrusivePtr(IntrusivePtr<U> other) noexcept : 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 {
150  // Some versions of GCC diagnose a maybe uninitialized variable here.
151  // Since we always initialize the field this should not be possible.
152 #pragma GCC diagnostic push;
153 #pragma GCC diagnostic ignored "-Wpragmas"
154 #pragma GCC diagnostic ignored "-Wunknown-warning-option"
155 #pragma GCC diagnostic ignored "-Wmaybe-uninitialized"
156  return ptr_;
157 #pragma GCC diagnostic pop;
158  }
159 
160  pointer operator->() const noexcept { return ptr_; }
161 
162  reference operator*() const noexcept { return *ptr_; }
163 
164  bool operator!() const noexcept { return ! ptr_; }
165 
166  explicit operator bool() const noexcept { return ptr_ != nullptr; }
167 
168 private:
169  pointer ptr_ = nullptr;
170 };
171 
180 template<class T, class... Ts>
182  // Assumes that objects start with a reference count of 1!
183  return {intrusive_ptr::AdoptRef{}, new T(std::forward<Ts>(args)...)};
184 }
185 
192 template<class T, class U>
194  return {intrusive_ptr::AdoptRef{}, static_cast<T*>(p.release())};
195 }
196 
197 // -- comparison to nullptr ----------------------------------------------------
198 
202 template<class T>
203 bool operator==(const IntrusivePtr<T>& x, std::nullptr_t) {
204  return ! x;
205 }
206 
210 template<class T>
211 bool operator==(std::nullptr_t, const IntrusivePtr<T>& x) {
212  return ! x;
213 }
214 
218 template<class T>
219 bool operator!=(const IntrusivePtr<T>& x, std::nullptr_t) {
220  return static_cast<bool>(x);
221 }
222 
226 template<class T>
227 bool operator!=(std::nullptr_t, const IntrusivePtr<T>& x) {
228  return static_cast<bool>(x);
229 }
230 
231 // -- comparison to raw pointer ------------------------------------------------
232 
236 template<class T>
237 bool operator==(const IntrusivePtr<T>& x, const T* y) {
238  return x.get() == y;
239 }
240 
244 template<class T>
245 bool operator==(const T* x, const IntrusivePtr<T>& y) {
246  return x == y.get();
247 }
248 
252 template<class T>
253 bool operator!=(const IntrusivePtr<T>& x, const T* y) {
254  return x.get() != y;
255 }
256 
260 template<class T>
261 bool operator!=(const T* x, const IntrusivePtr<T>& y) {
262  return x != y.get();
263 }
264 
265 // -- comparison to intrusive pointer ------------------------------------------
266 
267 // Using trailing return type and decltype() here removes this function from
268 // overload resolution if the two pointers types are not comparable (SFINAE).
269 
273 template<class T, class U>
274 auto operator==(const IntrusivePtr<T>& x, const IntrusivePtr<U>& y) -> decltype(x.get() == y.get()) {
275  return x.get() == y.get();
276 }
277 
281 template<class T, class U>
282 auto operator!=(const IntrusivePtr<T>& x, const IntrusivePtr<U>& y) -> decltype(x.get() != y.get()) {
283  return x.get() != y.get();
284 }
285 
286 } // namespace hilti::rt
IntrusivePtr(intrusive_ptr::NewRef, pointer raw_ptr) noexcept
Definition: intrusive-ptr.h:109
constexpr IntrusivePtr(intrusive_ptr::AdoptRef, pointer raw_ptr) noexcept
Definition: intrusive-ptr.h:99
Definition: intrusive-ptr.h:25
Definition: any.h:7
Definition: intrusive-ptr.h:19
Definition: intrusive-ptr.h:28
pointer release() noexcept
Definition: intrusive-ptr.h:142
IntrusivePtr< T > cast_intrusive(IntrusivePtr< U > p) noexcept
Definition: intrusive-ptr.h:193
IntrusivePtr< T > make_intrusive(Ts &&... args)
Definition: intrusive-ptr.h:181
Definition: scope.h:27
Definition: intrusive-ptr.h:69