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