c++-gtk-utils
intrusive_ptr.h
Go to the documentation of this file.
1 /* Copyright (C) 2006 to 2013 Chris Vine
2 
3 The library comprised in this file or of which this file is part is
4 distributed by Chris Vine under the GNU Lesser General Public
5 License as follows:
6 
7  This library is free software; you can redistribute it and/or
8  modify it under the terms of the GNU Lesser General Public License
9  as published by the Free Software Foundation; either version 2.1 of
10  the License, or (at your option) any later version.
11 
12  This library is distributed in the hope that it will be useful, but
13  WITHOUT ANY WARRANTY; without even the implied warranty of
14  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
15  Lesser General Public License, version 2.1, for more details.
16 
17  You should have received a copy of the GNU Lesser General Public
18  License, version 2.1, along with this library (see the file LGPL.TXT
19  which came with this source code package in the c++-gtk-utils
20  sub-directory); if not, write to the Free Software Foundation, Inc.,
21  51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
22 
23 However, it is not intended that the object code of a program whose
24 source code instantiates a template from this file or uses macros or
25 inline functions (of any length) should by reason only of that
26 instantiation or use be subject to the restrictions of use in the GNU
27 Lesser General Public License. With that in mind, the words "and
28 macros, inline functions and instantiations of templates (of any
29 length)" shall be treated as substituted for the words "and small
30 macros and small inline functions (ten lines or less in length)" in
31 the fourth paragraph of section 5 of that licence. This does not
32 affect any other reason why object code may be subject to the
33 restrictions in that licence (nor for the avoidance of doubt does it
34 affect the application of section 2 of that licence to modifications
35 of the source code in this file).
36 
37 */
38 
39 #ifndef CGU_INTRUSIVE_PTR_H
40 #define CGU_INTRUSIVE_PTR_H
41 
42 // define this if, instead of GLIB atomic funcions/memory barriers,
43 // you want to use a (slower) mutex to lock the reference count in the
44 // IntrusiveLockCounter class
45 /* #define CGU_INTRUSIVE_LOCK_COUNTER_USE_MUTEX 1 */
46 
47 #include <utility> // for std::move and std::swap
48 #include <functional> // for std::less and std::hash<T*>
49 #include <cstddef> // for std::size_t
50 
51 #ifdef CGU_INTRUSIVE_LOCK_COUNTER_USE_MUTEX
52 #include <c++-gtk-utils/mutex.h>
53 #else
54 #include <glib.h>
55 #endif
56 
58 
59 /**
60  * @addtogroup handles handles and smart pointers
61  */
62 
63 namespace Cgu {
64 
65 /**
66  * @class IntrusivePtr intrusive_ptr.h c++-gtk-utils/intrusive_ptr.h
67  * @brief This is a smart pointer for managing objects allocated on
68  * freestore which maintain their own reference count.
69  * @ingroup handles
70  *
71  * @details This is a class which manages objects which maintain their
72  * own reference count. It requires that the referenced object has
73  * two functions called ref() and unref(), which increment and
74  * decrement the reference count respectively. The IntrusiveCounter
75  * or IntrusiveLockCounter class can be inherited from to do this, but
76  * they do not have to be used. The IntrusiveLockCounter class is the
77  * same as the IntrusiveCounter class, except that it locks the
78  * reference count when it is incremented or decremented in order that
79  * IntrusivePtr objects in different threads can access the same
80  * object. (But only the reference count is locked, not the methods
81  * of the referenced object.)
82  *
83  * All the constructors (including the constructor which takes a raw
84  * pointer) increment the reference count, and the destructor
85  * decrements it and expects the referenced object to be deleted when
86  * the last IntrusivePtr referencing a particular object is destroyed.
87  * The IntrusiveCounter and IntrusiveLockCounter classes behave in
88  * this way. (This is different from the behaviour of GobjHandle
89  * smart pointers, which are constrained by the GObject reference
90  * counting system which begins with a reference count of 1 rather
91  * than 0, and of course different from normal shared pointer
92  * implementations for the same reason. The advantage of the approach
93  * with IntrusivePtr is that an already-managed object may safely be
94  * passed to the constructor taking a raw pointer without any
95  * additional steps being necessary.)
96  */
97 
98 template <class T> class IntrusivePtr {
99 
100  T* obj_p;
101 
102  void unreference() {
103  if (obj_p) obj_p->unref();
104  }
105 
106  void reference() {
107  if (obj_p) obj_p->ref();
108  }
109 
110 public:
111  /**
112  * This constructor does not throw.
113  * @param ptr The object which the IntrusivePtr is to manage (if
114  * any).
115  */
116  explicit IntrusivePtr(T* ptr = 0) {
117  obj_p = ptr;
118  reference();
119  }
120 
121  /**
122  * This copy constructor does not throw.
123  * @param intr_ptr The intrusive pointer to be copied.
124  */
125  IntrusivePtr(const IntrusivePtr& intr_ptr) {
126  obj_p = intr_ptr.obj_p;
127  reference();
128  }
129 
130  /**
131  * The move constructor does not throw. It has move semantics.
132  * @param intr_ptr The instrusive pointer to be moved.
133  */
135  obj_p = intr_ptr.obj_p;
136  intr_ptr.obj_p = 0;
137  }
138 
139  template <class U> friend class IntrusivePtr;
140 
141  /**
142  * A version of the copy constructor which enables pointer type
143  * conversion (assuming the type passed is implicitly type
144  * convertible to the managed type, such as a derived type). This
145  * copy constructor does not throw.
146  * @param intr_ptr The intrusive pointer to be copied.
147  */
148  template <class U> IntrusivePtr(const IntrusivePtr<U>& intr_ptr) {
149  obj_p = intr_ptr.obj_p;
150  reference();
151  }
152 
153  /**
154  * A version of the move constructor which enables pointer type
155  * conversion (assuming the type passed is implicitly type
156  * convertible to the managed type, such as a derived type). This
157  * move constructor does not throw.
158  * @param intr_ptr The intrusive pointer to be moved.
159  */
160  template <class U> IntrusivePtr(IntrusivePtr<U>&& intr_ptr) {
161  obj_p = intr_ptr.obj_p;
162  intr_ptr.obj_p = 0;
163  }
164 
165  /**
166  * This method (and so copy or move assignment) does not throw unless
167  * the destructor of a managed object throws.
168  * @param intr_ptr The assignee.
169  * @return The IntrusivePtr object after assignment.
170  */
171  // having a value type as the argument, rather than reference to const
172  // and then initialising a tmp object, gives the compiler more scope
173  // for optimisation, and also caters for r-values without a separate
174  // overload
176  std::swap(obj_p, intr_ptr.obj_p);
177  return *this;
178  }
179 
180  /**
181  * A version of the assignment operator which enables pointer type
182  * conversion (assuming the type passed is implicitly type
183  * convertible to the managed type, such as a derived type). This
184  * method does not throw unless the destructor of a managed object
185  * throws.
186  * @param intr_ptr The assignee.
187  * @return The IntrusivePtr object after assignment.
188  */
189  template <class U> IntrusivePtr& operator=(const IntrusivePtr<U>& intr_ptr) {
190  return operator=(IntrusivePtr(intr_ptr));
191  }
192 
193  /**
194  * A version of the operator for move assignment which enables
195  * pointer type conversion (assuming the type passed is implicitly
196  * type convertible to the managed type, such as a derived type).
197  * This method does not throw unless the destructor of a managed
198  * object throws.
199  * @param intr_ptr The intrusive pointer to be moved.
200  * @return The IntrusivePtr object after the move operation.
201  */
202  template <class U> IntrusivePtr& operator=(IntrusivePtr<U>&& intr_ptr) {
203  return operator=(IntrusivePtr(std::move(intr_ptr)));
204  }
205 
206 /**
207  * This method does not throw.
208  * @return A pointer to the managed object (or NULL if none is
209  * handled).
210  */
211  T* get() const {return obj_p;}
212 
213  /**
214  * This method does not throw.
215  * @return A reference to the managed object.
216  */
217  T& operator*() const {return *obj_p;}
218 
219  /**
220  * This method does not throw.
221  * @return A pointer to the managed object (or NULL if none is
222  * handled).
223  */
224  T* operator->() const {return obj_p;}
225 
226  /**
227  * Causes the intrusive pointer to cease to manage its managed
228  * object, deleting it if this is the last intrusive pointer managing
229  * it. If the argument passed is not NULL, the intrusive pointer
230  * will manage the new object passed. This method does not throw
231  * unless the destructor of a managed object throws.
232  * @param ptr NULL (the default), or a new object to manage.
233  */
234  void reset(T* ptr = 0) {
235  IntrusivePtr tmp(ptr);
236  std::swap(obj_p, tmp.obj_p);
237  }
238 
239  /**
240  * The destructor does not throw unless the destructor of a managed
241  * object throws - that should never happen.
242  */
243  ~IntrusivePtr() {unreference();}
244 };
245 
246 /**
247  * @class IntrusiveCounter intrusive_ptr.h c++-gtk-utils/intrusive_ptr.h
248  * @brief This is a counter class providing the ref() and unref()
249  * functions required by IntrusivePtr.
250  * @ingroup handles
251  * @sa IntrusiveLockCounter.
252  *
253  * This is a counter class providing the ref() and unref() functions
254  * required by IntrusivePtr. It is intended to be inherited from by
255  * classes which are to be managed by such a smart pointer.
256  */
257 
259  unsigned int count;
260 
261 public:
262  // we should not be able to copy objects of this class
263  // - objects, if constructed on the heap, should be passed
264  // via an IntrusivePtr object. An object of a derived class
265  // might still copy itself via its copy constructor and take
266  // along a new base IntrusiveCounter object constructed via
267  // the default constructor, and thus have a correct reference
268  // count of 0, but derived classes should not try to provide
269  // their own assignment operators.
270 /**
271  * This class cannot be copied. The copy constructor is deleted.
272  */
273  IntrusiveCounter(const IntrusiveCounter&) = delete;
274 /**
275  * This class cannot be copied. The assignment operator is deleted.
276  */
277  IntrusiveCounter& operator=(const IntrusiveCounter&) = delete;
278 
279 /**
280  * Increments the reference count. This method does not throw.
281  */
282  void ref() {++count;}
283 
284 /**
285  * Decrements the reference count, and if the count reaches 0 deletes
286  * itself (ie the managed object). This method does not throw unless
287  * the destructor of a derived class throws - that should never
288  * happen.
289  */
290  void unref() {
291  --count;
292  if (count == 0) delete this;
293  }
294 
295  IntrusiveCounter(): count(0) {}
296  virtual ~IntrusiveCounter() {}
297 };
298 
299 /**
300  * @class IntrusiveLockCounter intrusive_ptr.h c++-gtk-utils/intrusive_ptr.h
301  * @brief This is a counter class providing the ref() and unref()
302  * functions required by IntrusivePtr, with a thread safe reference
303  * count..
304  * @ingroup handles
305  * @sa IntrusiveCounter.
306  *
307  * This is a counter class providing the ref() and unref() functions
308  * required by IntrusivePtr. It is intended to be inherited from by
309  * classes which are to be managed by such a smart pointer, and
310  * includes synchronization so that such an inheriting class object
311  * can be accessed by different IntrusivePtr objects in different
312  * threads (although the word Lock is in the title, by default it uses
313  * glib atomic functions to access the reference count rather than a
314  * mutex, so the overhead should be very small). Note that only the
315  * reference count is protected, so this is thread safe in the sense
316  * in which a raw pointer is thread safe.
317  *
318  * As mentioned, by default glib atomic functions are used to provide
319  * thread-safe manipulation of the reference count. However, the
320  * symbol CGU_INTRUSIVE_LOCK_COUNTER_USE_MUTEX can be defined so that
321  * the library uses mutexes instead, which might be useful for some
322  * debugging purposes. Note that if
323  * CGU_INTRUSIVE_LOCK_COUNTER_USE_MUTEX is to be defined, this is best
324  * done by textually amending the intrusive_ptr.h header file before
325  * the library is compiled. This will ensure that everything in the
326  * program and the library which includes the intrusive_ptr.h header
327  * is guaranteed to see the same definitions so that the C++
328  * standard's one-definition-rule is complied with.
329  */
330 
332 #ifdef CGU_INTRUSIVE_LOCK_COUNTER_USE_MUTEX
333  unsigned int count;
334  Thread::Mutex mutex;
335 #else
336  gint count;
337 #endif
338 
339 public:
340  // we should not be able to copy objects of this class
341  // - objects, if constructed on the heap, should be passed
342  // via an IntrusivePtr object. An object of a derived class
343  // might still copy itself via its copy constructor and take
344  // along a new base IntrusiveLockCounter object constructed via
345  // the default constructor, and thus have a correct reference
346  // count of 0, but derived classes should not try to provide
347  // their own assignment operators.
348 /**
349  * This class cannot be copied. The copy constructor is deleted.
350  */
352 /**
353  * This class cannot be copied. The assignment operator is deleted.
354  */
356 
357 /**
358  * Increments the reference count. This method does not throw.
359  */
360  void ref() {
361 #ifdef CGU_INTRUSIVE_LOCK_COUNTER_USE_MUTEX
362  Thread::Mutex::Lock lock(mutex);
363  ++count;
364 #else
365  g_atomic_int_inc(&count);
366 #endif
367  }
368 
369 /**
370  * Decrements the reference count, and if the count reaches 0 deletes
371  * itself (ie the managed object). This method does not throw unless
372  * the destructor of a derived class throws - that should never
373  * happen.
374  */
375  void unref() {
376 #ifdef CGU_INTRUSIVE_LOCK_COUNTER_USE_MUTEX
377  mutex.lock();
378  --count;
379  if (count == 0) {
380  mutex.unlock();
381  delete this;
382  }
383  else mutex.unlock();
384 #else
385  if (g_atomic_int_dec_and_test(&count)) {
386  delete this;
387  }
388 #endif
389  }
390 
391 /**
392  * By default, glib atomic functions are used to provide thread-safe
393  * manipulation of the reference count. However, the header file
394  * intrusive_ptr.h can be textually amended before the library is
395  * compiled to define the symbol CGU_INTRUSIVE_LOCK_COUNTER_USE_MUTEX
396  * so as to use mutexes instead, which might be useful for some
397  * debugging purposes. Were that to be done, Cgu::Thread::MutexError
398  * might be thrown by this constructor if initialization of the mutex
399  * fails.
400  *
401  * Otherwise, this constructor does not throw.
402  */
403  IntrusiveLockCounter(): count(0) {}
404 
405 /**
406  * This destructor does not throw, unless the destructor of a derived
407  * class throws.
408  */
410 };
411 
412 #if defined(CGU_USE_SMART_PTR_COMPARISON) || defined(DOXYGEN_PARSING)
413 
414 // we can use built-in operator == when comparing pointers referencing
415 // different objects of the same type
416 /**
417  * @ingroup handles
418  *
419  * This comparison operator does not throw. It compares the addresses
420  * of the managed objects.
421  *
422  * Since 2.0.0-rc2
423  */
424 template <class T>
425 bool operator==(const IntrusivePtr<T>& s1, const IntrusivePtr<T>& s2) {
426  return (s1.get() == s2.get());
427 }
428 
429 /**
430  * @ingroup handles
431  *
432  * This comparison operator does not throw. It compares the addresses
433  * of the managed objects.
434  *
435  * Since 2.0.0-rc2
436  */
437 template <class T>
438 bool operator!=(const IntrusivePtr<T>& s1, const IntrusivePtr<T>& s2) {
439  return !(s1 == s2);
440 }
441 
442 // we must use std::less rather than the < built-in operator for
443 // pointers to objects not within the same array or object: "For
444 // templates greater, less, greater_equal, and less_equal, the
445 // specializations for any pointer type yield a total order, even if
446 // the built-in operators <, >, <=, >= do not." (para 20.3.3/8).
447 /**
448  * @ingroup handles
449  *
450  * This comparison operator does not throw. It compares the addresses
451  * of the managed objects.
452  *
453  * Since 2.0.0-rc2
454  */
455 template <class T>
456 bool operator<(const IntrusivePtr<T>& s1, const IntrusivePtr<T>& s2) {
457  return std::less<T*>()(s1.get(), s2.get());
458 }
459 
460 #endif // CGU_USE_SMART_PTR_COMPARISON
461 
462 } // namespace Cgu
463 
464 // doxygen produces long filenames that tar can't handle:
465 // we have generic documentation for std::hash specialisations
466 // in doxygen.main.in
467 #if defined(CGU_USE_SMART_PTR_COMPARISON) && !defined(DOXYGEN_PARSING)
468 /* This struct allows InstrusivePtr objects to be keys in unordered
469  associative containers */
470 namespace std {
471 template <class T>
472 struct hash<Cgu::IntrusivePtr<T>> {
473  typedef std::size_t result_type;
474  typedef Cgu::IntrusivePtr<T> argument_type;
475  result_type operator()(const argument_type& s) const {
476  // this is fine: std::hash structs do not normally contain data and
477  // std::hash<T*> certainly won't, so we don't have overhead constructing
478  // std::hash<T*> on the fly
479  return std::hash<T*>()(s.get());
480  }
481 };
482 } // namespace std
483 #endif // CGU_USE_SMART_PTR_COMPARISON
484 
485 #endif