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 { 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
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:184
IntrusivePtr< T > make_intrusive(Ts &&... args)
Definition: intrusive-ptr.h:172
Definition: scope.h:25
Definition: intrusive-ptr.h:69