c++-gtk-utils

- Main Page
- Related Pages
- Modules
- Namespaces
- Classes
- Files

- File List
- File Members

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
57 #include <c++-gtk-utils/cgu_config.h>
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 */
134 IntrusivePtr(IntrusivePtr&& intr_ptr) {
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
175 IntrusivePtr& operator=(IntrusivePtr intr_ptr) {
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
258 class IntrusiveCounter {
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
331 class IntrusiveLockCounter {
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 */
351 IntrusiveLockCounter(const IntrusiveLockCounter&) = delete;
352 /**
353 * This class cannot be copied. The assignment operator is deleted.
354 */
355 IntrusiveLockCounter& operator=(const IntrusiveLockCounter&) = delete;
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 */
409 virtual ~IntrusiveLockCounter() {}
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