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 <cstdint>
8 #include <iostream>
9 #include <type_traits>
10 #include <utility>
11 
12 namespace hilti::rt {
13 
14 namespace intrusive_ptr {
15 
20 struct AdoptRef {};
21 
26 struct NewRef {};
27 
30 private:
31  template<typename T>
32  friend void Ref(const T*);
33  template<typename T>
34  friend void Unref(const T*);
35  mutable uint64_t _references = 1;
36 };
37 
38 template<typename T>
39 inline void Ref(const T* m) {
40  if ( m )
41  ++m->_references;
42 }
43 
44 template<typename T>
45 inline void Unref(const T* m) {
46  if ( m && --m->_references == 0 )
47  delete m;
48 }
49 
50 } // namespace intrusive_ptr
51 
69 template<class T>
70 class IntrusivePtr {
71 public:
72  // -- member types
73 
74  using pointer = T*;
75 
76  using const_pointer = const T*;
77 
78  using element_type = T;
79 
80  using reference = T&;
81 
82  using const_reference = const T&;
83 
84  // -- constructors, destructors, and assignment operators
85 
86  constexpr IntrusivePtr() noexcept = default;
87 
88  constexpr IntrusivePtr(std::nullptr_t) noexcept : IntrusivePtr() {
89  // nop
90  }
91 
100  constexpr IntrusivePtr(intrusive_ptr::AdoptRef, pointer raw_ptr) noexcept : ptr_(raw_ptr) {}
101 
110  IntrusivePtr(intrusive_ptr::NewRef, pointer raw_ptr) noexcept : ptr_(raw_ptr) {
111  if ( ptr_ )
112  Ref(ptr_);
113  }
114 
115  IntrusivePtr(IntrusivePtr&& other) noexcept : ptr_(other.release()) {
116  // nop
117  }
118 
119  IntrusivePtr(const IntrusivePtr& other) noexcept : IntrusivePtr(intrusive_ptr::NewRef{}, other.get()) {}
120 
121  template<class U, class = std::enable_if_t<std::is_convertible_v<U*, T*>>>
122  IntrusivePtr(IntrusivePtr<U> other) noexcept : ptr_(other.release()) {
123  // nop
124  }
125 
126  ~IntrusivePtr() {
127  if ( ptr_ )
128  Unref(ptr_);
129  }
130 
131  void swap(IntrusivePtr& other) noexcept { std::swap(ptr_, other.ptr_); }
132 
133  friend void swap(IntrusivePtr& a, IntrusivePtr& b) noexcept {
134  using std::swap;
135  swap(a.ptr_, b.ptr_);
136  }
137 
143  pointer release() noexcept { return std::exchange(ptr_, nullptr); }
144 
145  IntrusivePtr& operator=(IntrusivePtr other) noexcept {
146  swap(other);
147  return *this;
148  }
149 
150  pointer get() const noexcept {
151  // Some versions of GCC diagnose a maybe uninitialized variable here.
152  // Since we always initialize the field this should not be possible.
153 #pragma GCC diagnostic push;
154 #pragma GCC diagnostic ignored "-Wpragmas"
155 #pragma GCC diagnostic ignored "-Wunknown-warning-option"
156 #pragma GCC diagnostic ignored "-Wmaybe-uninitialized"
157  return ptr_;
158 #pragma GCC diagnostic pop;
159  }
160 
161  pointer operator->() const noexcept { return ptr_; }
162 
163  reference operator*() const noexcept { return *ptr_; }
164 
165  bool operator!() const noexcept { return ! ptr_; }
166 
167  explicit operator bool() const noexcept { return ptr_ != nullptr; }
168 
169 private:
170  pointer ptr_ = nullptr;
171 };
172 
181 template<class T, class... Ts>
183  // Assumes that objects start with a reference count of 1!
184  return {intrusive_ptr::AdoptRef{}, new T(std::forward<Ts>(args)...)};
185 }
186 
193 template<class T, class U>
195  return {intrusive_ptr::AdoptRef{}, static_cast<T*>(p.release())};
196 }
197 
198 // -- comparison to nullptr ----------------------------------------------------
199 
203 template<class T>
204 bool operator==(const IntrusivePtr<T>& x, std::nullptr_t) {
205  return ! x;
206 }
207 
211 template<class T>
212 bool operator==(std::nullptr_t, const IntrusivePtr<T>& x) {
213  return ! x;
214 }
215 
219 template<class T>
220 bool operator!=(const IntrusivePtr<T>& x, std::nullptr_t) {
221  return static_cast<bool>(x);
222 }
223 
227 template<class T>
228 bool operator!=(std::nullptr_t, const IntrusivePtr<T>& x) {
229  return static_cast<bool>(x);
230 }
231 
232 // -- comparison to raw pointer ------------------------------------------------
233 
237 template<class T>
238 bool operator==(const IntrusivePtr<T>& x, const T* y) {
239  return x.get() == y;
240 }
241 
245 template<class T>
246 bool operator==(const T* x, const IntrusivePtr<T>& y) {
247  return x == y.get();
248 }
249 
253 template<class T>
254 bool operator!=(const IntrusivePtr<T>& x, const T* y) {
255  return x.get() != y;
256 }
257 
261 template<class T>
262 bool operator!=(const T* x, const IntrusivePtr<T>& y) {
263  return x != y.get();
264 }
265 
266 // -- comparison to intrusive pointer ------------------------------------------
267 
268 // Using trailing return type and decltype() here removes this function from
269 // overload resolution if the two pointers types are not comparable (SFINAE).
270 
274 template<class T, class U>
275 auto operator==(const IntrusivePtr<T>& x, const IntrusivePtr<U>& y) -> decltype(x.get() == y.get()) {
276  return x.get() == y.get();
277 }
278 
282 template<class T, class U>
283 auto operator!=(const IntrusivePtr<T>& x, const IntrusivePtr<U>& y) -> decltype(x.get() != y.get()) {
284  return x.get() != y.get();
285 }
286 
287 } // namespace hilti::rt
IntrusivePtr(intrusive_ptr::NewRef, pointer raw_ptr) noexcept
Definition: intrusive-ptr.h:110
constexpr IntrusivePtr(intrusive_ptr::AdoptRef, pointer raw_ptr) noexcept
Definition: intrusive-ptr.h:100
Definition: intrusive-ptr.h:26
Definition: any.h:7
Definition: intrusive-ptr.h:20
Definition: intrusive-ptr.h:29
pointer release() noexcept
Definition: intrusive-ptr.h:143
IntrusivePtr< T > cast_intrusive(IntrusivePtr< U > p) noexcept
Definition: intrusive-ptr.h:194
IntrusivePtr< T > make_intrusive(Ts &&... args)
Definition: intrusive-ptr.h:182
Definition: scope.h:27
Definition: intrusive-ptr.h:70