Thrill  0.1
 All Classes Namespaces Files Functions Variables Typedefs Enumerations Enumerator Friends Macros Groups Pages
counting_ptr.hpp
Go to the documentation of this file.
1 /*******************************************************************************
2  * tlx/counting_ptr.hpp
3  *
4  * Part of tlx - http://panthema.net/tlx
5  *
6  * Copyright (C) 2013-2017 Timo Bingmann <[email protected]>
7  *
8  * All rights reserved. Published under the Boost Software License, Version 1.0
9  ******************************************************************************/
10 
11 #ifndef TLX_COUNTING_PTR_HEADER
12 #define TLX_COUNTING_PTR_HEADER
13 
14 #include <algorithm>
15 #include <atomic>
16 #include <cassert>
17 #include <iosfwd>
18 #include <type_traits>
19 #include <utility>
20 
21 namespace tlx {
22 
23 //! default deleter for CountingPtr
25 {
26 public:
27  template <typename Type>
28  void operator () (Type* ptr) const noexcept {
29  delete ptr;
30  }
31 };
32 
33 //! dummy deleter for CountingPtr
35 {
36 public:
37  template <typename Type>
38  void operator () (Type*) const noexcept { }
39 };
40 
41 /*!
42  * High-performance smart pointer used as a wrapping reference counting pointer.
43  *
44  * This smart pointer class requires two functions in the template type: void
45  * inc_reference() and void dec_reference(). These must increment and decrement
46  * a reference count inside the templated object. When initialized, the type
47  * must have reference count zero. Each new object referencing the data calls
48  * inc_reference() and each destroying holder calls del_reference(). When the
49  * data object determines that it's internal count is zero, then it must destroy
50  * itself.
51  *
52  * Accompanying the CountingPtr is a class ReferenceCounter, from which
53  * reference counted classes may be derive from. The class ReferenceCounter
54  * implement all methods required for reference counting.
55  *
56  * The whole method is more similar to boost's instrusive_ptr, but also yields
57  * something resembling std::shared_ptr. However, compared to std::shared_ptr,
58  * this class only contains a single pointer, while shared_ptr contains two
59  * which are only related if constructed with std::make_shared.
60  *
61  * Another advantage with this method is that no kludges like
62  * std::enable_shared_from_this are needed.
63  */
64 template <typename Type, typename Deleter = CountingPtrDefaultDeleter>
66 {
67 public:
68  //! contained type.
69  using element_type = Type;
70 
71 private:
72  //! the pointer to the currently referenced object.
74 
75  //! increment reference count of object.
76  void inc_reference(Type* o) noexcept
77  { if (o) o->inc_reference(); }
78 
79  //! decrement reference count of current object and maybe delete it.
80  void dec_reference() noexcept {
81  if (ptr_ && ptr_->dec_reference())
82  Deleter()(ptr_);
83  }
84 
85 public:
86  //! all CountingPtr are friends such that they may steal pointers.
87  template <typename Other, typename OtherDeleter>
88  friend class CountingPtr;
89 
90  //! default constructor: contains a nullptr pointer.
91  CountingPtr() noexcept
92  : ptr_(nullptr) { }
93 
94  //! implicit conversion from nullptr_t: contains a nullptr pointer.
95  CountingPtr(std::nullptr_t) noexcept // NOLINT
96  : ptr_(nullptr) { }
97 
98  //! constructor from pointer: initializes new reference to ptr.
99  explicit CountingPtr(Type* ptr) noexcept
100  : ptr_(ptr)
101  { inc_reference(ptr_); }
102 
103  //! copy-constructor: also initializes new reference to ptr.
104  CountingPtr(const CountingPtr& other) noexcept
105  : ptr_(other.ptr_)
106  { inc_reference(ptr_); }
107 
108  //! copy-constructor: also initializes new reference to ptr.
109  template <typename Subclass,
110  typename = typename std::enable_if<
113  : ptr_(other.ptr_)
114  { inc_reference(ptr_); }
115 
116  //! move-constructor: just moves pointer, does not change reference counts.
117  CountingPtr(CountingPtr&& other) noexcept
118  : ptr_(other.ptr_)
119  { other.ptr_ = nullptr; }
120 
121  //! move-constructor: just moves pointer, does not change reference counts.
122  template <typename Subclass,
123  typename = typename std::enable_if<
126  : ptr_(other.ptr_)
127  { other.ptr_ = nullptr; }
128 
129  //! copy-assignment operator: acquire reference on new one and dereference
130  //! current object.
131  CountingPtr& operator = (const CountingPtr& other) noexcept {
132  if (ptr_ == other.ptr_)
133  return *this;
134  inc_reference(other.ptr_);
135  dec_reference();
136  ptr_ = other.ptr_;
137  return *this;
138  }
139 
140  //! copy-assignment operator: acquire reference on new one and dereference
141  //! current object.
142  template <typename Subclass,
143  typename = typename std::enable_if<
145  CountingPtr&
147  if (ptr_ == other.ptr_)
148  return *this;
149  inc_reference(other.ptr_);
150  dec_reference();
151  ptr_ = other.ptr_;
152  return *this;
153  }
154 
155  //! move-assignment operator: move reference of other to current object.
156  CountingPtr& operator = (CountingPtr&& other) noexcept {
157  if (ptr_ == other.ptr_)
158  return *this;
159  dec_reference();
160  ptr_ = other.ptr_;
161  other.ptr_ = nullptr;
162  return *this;
163  }
164 
165  //! move-assignment operator: move reference of other to current object.
166  template <typename Subclass,
167  typename = typename std::enable_if<
170  if (ptr_ == other.ptr_)
171  return *this;
172  dec_reference();
173  ptr_ = other.ptr_;
174  other.ptr_ = nullptr;
175  return *this;
176  }
177 
178  //! destructor: decrements reference count in ptr.
180 
181  //! return the enclosed object as reference.
182  Type& operator * () const noexcept {
183  assert(ptr_);
184  return *ptr_;
185  }
186 
187  //! return the enclosed pointer.
188  Type* operator -> () const noexcept {
189  assert(ptr_);
190  return ptr_;
191  }
192 
193  //! return the enclosed pointer.
194  Type * get() const noexcept { return ptr_; }
195 
196  //! test equality of only the pointer values.
197  bool operator == (const CountingPtr& other) const noexcept
198  { return ptr_ == other.ptr_; }
199 
200  //! test inequality of only the pointer values.
201  bool operator != (const CountingPtr& other) const noexcept
202  { return ptr_ != other.ptr_; }
203 
204  //! test equality of only the address pointed to
205  bool operator == (Type* other) const noexcept
206  { return ptr_ == other; }
207 
208  //! test inequality of only the address pointed to
209  bool operator != (Type* other) const noexcept
210  { return ptr_ != other; }
211 
212  //! test for a non-nullptr pointer
213  bool valid() const noexcept
214  { return (ptr_ != nullptr); }
215 
216  //! cast to bool checks for a nullptr pointer
217  operator bool () const noexcept
218  { return valid(); }
219 
220  //! test for a nullptr pointer
221  bool empty() const noexcept
222  { return (ptr_ == nullptr); }
223 
224  //! if the object is referred by this CountingPtr only
225  bool unique() const noexcept
226  { return ptr_ && ptr_->unique(); }
227 
228  //! make and refer a copy if the original object was shared.
229  void unify() {
230  if (ptr_ && !ptr_->unique())
231  operator = (CountingPtr(new Type(*ptr_)));
232  }
233 
234  //! release contained pointer, frees object if this is the last reference.
235  void reset() {
236  dec_reference();
237  ptr_ = nullptr;
238  }
239 
240  //! swap enclosed object with another counting pointer (no reference counts
241  //! need change)
242  void swap(CountingPtr& b) noexcept
243  { std::swap(ptr_, b.ptr_); }
244 };
245 
246 //! make alias due to similarity with std::shared_ptr<T>
247 template <typename Type>
249 
250 //! make alias for dummy deleter
251 template <typename Type>
253 
254 //! method analogous to std::make_shared and std::make_unique.
255 template <typename Type, typename... Args>
257  return CountingPtr<Type>(new Type(std::forward<Args>(args) ...));
258 }
259 
260 //! swap enclosed object with another counting pointer (no reference counts need
261 //! change)
262 template <typename A, typename D>
263 void swap(CountingPtr<A, D>& a1, CountingPtr<A, D>& a2) noexcept {
264  a1.swap(a2);
265 }
266 
267 //! print pointer
268 template <typename A, typename D>
269 std::ostream& operator << (std::ostream& os, const CountingPtr<A, D>& c) {
270  return os << c.get();
271 }
272 
273 /*!
274  * Provides reference counting abilities for use with CountingPtr.
275  *
276  * Use as superclass of the actual object, this adds a reference_count_
277  * value. Then either use CountingPtr as pointer to manage references and
278  * deletion, or just do normal new and delete.
279  */
281 {
282 private:
283  //! the reference count is kept mutable for CountingPtr<const Type> to
284  //! change the reference count.
285  mutable std::atomic<size_t> reference_count_;
286 
287 public:
288  //! new objects have zero reference count
289  ReferenceCounter() noexcept
290  : reference_count_(0) { }
291 
292  //! coping still creates a new object with zero reference count
294  : reference_count_(0) { }
295 
296  //! assignment operator, leaves pointers unchanged
298  // changing the contents leaves pointers unchanged
299  return *this;
300  }
301 
303  { assert(reference_count_ == 0); }
304 
305 public:
306  //! Call whenever setting a pointer to the object.
307  void inc_reference() const noexcept
308  { ++reference_count_; }
309 
310  /*!
311  * Call whenever resetting (i.e. overwriting) a pointer to the object.
312  * IMPORTANT: In case of self-assignment, call AFTER inc_reference().
313  *
314  * \return if the object has to be deleted (i.e. if it's reference count
315  * dropped to zero)
316  */
317  bool dec_reference() const noexcept {
318  assert(reference_count_ > 0);
319  return (--reference_count_ == 0);
320  }
321 
322  //! Test if the ReferenceCounter is referenced by only one CountingPtr.
323  bool unique() const noexcept
324  { return (reference_count_ == 1); }
325 
326  //! Return the number of references to this object (for debugging)
327  size_t reference_count() const noexcept
328  { return reference_count_; }
329 };
330 
331 //! make alias due to CountingPtr's similarity with std::shared_ptr<T>
333 
334 /** \page tlx_counting_ptr CountingPtr – an intrusive reference counting pointer
335 
336 \brief \ref CountingPtr is an implementation of <b>intrusive reference counting</b>.
337 This is similar, but not identical to boost or C++ TR1's \c
338 shared_ptr. Intrusive reference counting requires the counted class to contain
339 the counter, which is not required by <tt>std::shared_ptr</tt>.
340 
341 Intrusive counting is often faster due to fewer cache faults. Furthermore, \ref
342 CountingPtr is a <b>single pointer</b>, whereas <tt>std::shared_ptr</tt>
343 actually contains (at least) two pointers. \ref CountingPtr also creates a lot
344 less debug info due to reduced complexity.
345 
346 \ref CountingPtr is accompanied by \ref ReferenceCounter, which contains the
347 actual reference counter (a single integer). A reference counted object must
348 derive from \ref ReferenceCounter :
349 
350 \code
351 struct Something : public tlx::ReferenceCounter
352 {
353 };
354 \endcode
355 
356 Code that now wishes to use pointers referencing this object, will typedef an
357 \ref CountingPtr, which is used to increment and decrement the included
358 reference counter automatically.
359 
360 \code
361 using SomethingPtr = tlx::CountingPtr<Something>;
362 {
363  // create new instance of something
364  SomethingPtr p1 = new something;
365  {
366  // create a new reference to the same instance (no deep copy!)
367  SomethingPtr p2 = p1;
368  // this block end will decrement the reference count, but not delete the object
369  }
370  // this block end will delete the object
371 }
372 \endcode
373 
374 The \ref CountingPtr can generally be used like a usual pointer or \c
375 std::shared_ptr (see the docs for more).
376 
377 */
378 
379 } // namespace tlx
380 
381 #endif // !TLX_COUNTING_PTR_HEADER
382 
383 /******************************************************************************/
void unify()
make and refer a copy if the original object was shared.
CountingPtr< Type > make_counting(Args &&...args)
method analogous to std::make_shared and std::make_unique.
WriteStream element_type
contained type.
CountingPtr(Type *ptr) noexcept
constructor from pointer: initializes new reference to ptr.
bool operator==(const CountingPtr &other) const noexcept
test equality of only the pointer values.
bool unique() const noexcept
Test if the ReferenceCounter is referenced by only one CountingPtr.
dummy deleter for CountingPtr
Type * operator->() const noexcept
return the enclosed pointer.
Type
VFS object type.
Definition: file_io.hpp:52
void operator()(Type *ptr) const noexcept
CountingPtr(const CountingPtr< Subclass, Deleter > &other) noexcept
copy-constructor: also initializes new reference to ptr.
ReferenceCounter & operator=(const ReferenceCounter &) noexcept
assignment operator, leaves pointers unchanged
bool operator!=(const CountingPtr &other) const noexcept
test inequality of only the pointer values.
void reset()
release contained pointer, frees object if this is the last reference.
default deleter for CountingPtr
bool valid() const noexcept
test for a non-nullptr pointer
ReferenceCounter(const ReferenceCounter &) noexcept
coping still creates a new object with zero reference count
Type * ptr_
the pointer to the currently referenced object.
CountingPtr() noexcept
default constructor: contains a nullptr pointer.
CountingPtr(CountingPtr &&other) noexcept
move-constructor: just moves pointer, does not change reference counts.
CountingPtr(const CountingPtr &other) noexcept
copy-constructor: also initializes new reference to ptr.
std::atomic< size_t > reference_count_
CountingPtr & operator=(const CountingPtr &other) noexcept
ReferenceCounter() noexcept
new objects have zero reference count
bool dec_reference() const noexcept
Call whenever resetting (i.e.
void swap(CountingPtr< A, D > &a1, CountingPtr< A, D > &a2) noexcept
int value
Definition: gen_data.py:41
size_t reference_count() const noexcept
Return the number of references to this object (for debugging)
bool unique() const noexcept
if the object is referred by this CountingPtr only
void inc_reference(Type *o) noexcept
increment reference count of object.
High-performance smart pointer used as a wrapping reference counting pointer.
bool empty() const noexcept
test for a nullptr pointer
~CountingPtr()
destructor: decrements reference count in ptr.
void inc_reference() const noexcept
Call whenever setting a pointer to the object.
void dec_reference() noexcept
decrement reference count of current object and maybe delete it.
CountingPtr(CountingPtr< Subclass, Deleter > &&other) noexcept
move-constructor: just moves pointer, does not change reference counts.
void operator()(Type *) const noexcept
Type & operator*() const noexcept
return the enclosed object as reference.
CountingPtr(std::nullptr_t) noexcept
implicit conversion from nullptr_t: contains a nullptr pointer.
Provides reference counting abilities for use with CountingPtr.
void swap(CountingPtr &b) noexcept