c++-gtk-utils: intrusive_ptr.h Source File

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 unless the managed object's ref()
113 * function throws (which would not happen with any sane
114 * implementation, and does not happen with the IntrusiveCounter and
115 * IntrusiveLockCounter classes).
116 * @param ptr The object which the IntrusivePtr is to manage (if
117 * any).
118 */
119 explicit IntrusivePtr(T* ptr = 0) {
120 obj_p = ptr;
121 reference();
122 }
123
124 /**
125 * This copy constructor does not throw unless the managed object's
126 * ref() function throws (which would not happen with any sane
127 * implementation, and does not happen with the IntrusiveCounter and
128 * IntrusiveLockCounter classes).
129 * @param intr_ptr The intrusive pointer to be copied.
130 */
131 IntrusivePtr(const IntrusivePtr& intr_ptr) {
132 obj_p = intr_ptr.obj_p;
133 reference();
134 }
135
136 /**
137 * The move constructor does not throw. It has move semantics.
138 * @param intr_ptr The instrusive pointer to be moved.
139 */
140 IntrusivePtr(IntrusivePtr&& intr_ptr) noexcept {
141 obj_p = intr_ptr.obj_p;
142 intr_ptr.obj_p = 0;
143 }
144
145 template <class U> friend class IntrusivePtr;
146
147 /**
148 * A version of the copy constructor which enables pointer type
149 * conversion (assuming the type passed is implicitly type
150 * convertible to the managed type, such as a derived type). This
151 * copy constructor does not throw unless the managed object's ref()
152 * function throws (which would not happen with any sane
153 * implementation, and does not happen with the IntrusiveCounter and
154 * IntrusiveLockCounter classes).
155 * @param intr_ptr The intrusive pointer to be copied.
156 */
157 template <class U> IntrusivePtr(const IntrusivePtr<U>& intr_ptr) {
158 obj_p = intr_ptr.obj_p;
159 reference();
160 }
161
162 /**
163 * A version of the move constructor which enables pointer type
164 * conversion (assuming the type passed is implicitly type
165 * convertible to the managed type, such as a derived type). This
166 * move constructor does not throw.
167 * @param intr_ptr The intrusive pointer to be moved.
168 */
169 template <class U> IntrusivePtr(IntrusivePtr<U>&& intr_ptr) noexcept {
170 obj_p = intr_ptr.obj_p;
171 intr_ptr.obj_p = 0;
172 }
173
174 /**
175 * This method (and so copy or move assignment) does not throw unless
176 * the unref() function or destructor of a managed object throws.
177 * @param intr_ptr The assignee.
178 * @return The IntrusivePtr object after assignment.
179 */
180 // having a value type as the argument, rather than reference to const
181 // and then initialising a tmp object, gives the compiler more scope
182 // for optimisation, and also caters for r-values without a separate
183 // overload
184 IntrusivePtr& operator=(IntrusivePtr intr_ptr) {
185 std::swap(obj_p, intr_ptr.obj_p);
186 return *this;
187 }
188
189 /**
190 * A version of the assignment operator which enables pointer type
191 * conversion (assuming the type passed is implicitly type
192 * convertible to the managed type, such as a derived type). This
193 * method does not throw unless the unref() function or destructor of
194 * a managed object throws.
195 * @param intr_ptr The assignee.
196 * @return The IntrusivePtr object after assignment.
197 */
198 template <class U> IntrusivePtr& operator=(const IntrusivePtr<U>& intr_ptr) {
199 return operator=(IntrusivePtr(intr_ptr));
200 }
201
202 /**
203 * A version of the operator for move assignment which enables
204 * pointer type conversion (assuming the type passed is implicitly
205 * type convertible to the managed type, such as a derived type).
206 * This method does not throw unless the unref() function or
207 * destructor of a managed object throws.
208 * @param intr_ptr The intrusive pointer to be moved.
209 * @return The IntrusivePtr object after the move operation.
210 */
211 template <class U> IntrusivePtr& operator=(IntrusivePtr<U>&& intr_ptr) {
212 return operator=(IntrusivePtr(std::move(intr_ptr)));
213 }
214
215 /**
216 * This method does not throw.
217 * @return A pointer to the managed object (or NULL if none is
218 * handled).
219 */
220 T* get() const noexcept {return obj_p;}
221
222 /**
223 * This method does not throw.
224 * @return A reference to the managed object.
225 */
226 T& operator*() const noexcept {return *obj_p;}
227
228 /**
229 * This method does not throw.
230 * @return A pointer to the managed object (or NULL if none is
231 * handled).
232 */
233 T* operator->() const noexcept {return obj_p;}
234
235 /**
236 * Causes the intrusive pointer to cease to manage its managed
237 * object, deleting it if this is the last intrusive pointer managing
238 * it. If the argument passed is not NULL, the intrusive pointer
239 * will manage the new object passed. This method does not throw
240 * unless the the unref() function or destructor of a managed object
241 * throws.
242 * @param ptr NULL (the default), or a new object to manage.
243 */
244 void reset(T* ptr = 0) {
245 IntrusivePtr tmp(ptr);
246 std::swap(obj_p, tmp.obj_p);
247 }
248
249 /**
250 * The destructor does not throw unless the destructor of a managed
251 * object throws - that should never happen.
252 */
253 ~IntrusivePtr() {unreference();}
254 };
255
256 /**
257 * @class IntrusiveCounter intrusive_ptr.h c++-gtk-utils/intrusive_ptr.h
258 * @brief This is a counter class providing the ref() and unref()
259 * functions required by IntrusivePtr.
260 * @ingroup handles
261 * @sa IntrusiveLockCounter.
262 *
263 * This is a counter class providing the ref() and unref() functions
264 * required by IntrusivePtr. It is intended to be inherited from by
265 * classes which are to be managed by such a smart pointer.
266 */
267
268 class IntrusiveCounter {
269 unsigned int count;
270
271 public:
272 // we should not be able to copy objects of this class
273 // - objects, if constructed on the heap, should be passed
274 // via an IntrusivePtr object. An object of a derived class
275 // might still copy itself via its copy constructor and take
276 // along a new base IntrusiveCounter object constructed via
277 // the default constructor, and thus have a correct reference
278 // count of 0, but derived classes should not try to provide
279 // their own assignment operators.
280 /**
281 * This class cannot be copied. The copy constructor is deleted.
282 */
283 IntrusiveCounter(const IntrusiveCounter&) = delete;
284 /**
285 * This class cannot be copied. The assignment operator is deleted.
286 */
287 IntrusiveCounter& operator=(const IntrusiveCounter&) = delete;
288
289 /**
290 * Increments the reference count. This method does not throw.
291 */
292 void ref() noexcept {++count;}
293
294 /**
295 * Decrements the reference count, and if the count reaches 0 deletes
296 * itself (ie the managed object). This method does not throw unless
297 * the destructor of a derived class throws - that should never
298 * happen.
299 */
300 void unref() {
301 --count;
302 if (count == 0) delete this;
303 }
304
305 /**
306 * The constructor does not throw.
307 */
308 IntrusiveCounter() noexcept: count(0) {}
309
310 /**
311 * This destructor does not throw, unless the destructor of a derived
312 * class throws - that should never happen.
313 */
314 virtual ~IntrusiveCounter() {}
315 };
316
317 /**
318 * @class IntrusiveLockCounter intrusive_ptr.h c++-gtk-utils/intrusive_ptr.h
319 * @brief This is a counter class providing the ref() and unref()
320 * functions required by IntrusivePtr, with a thread safe reference
321 * count..
322 * @ingroup handles
323 * @sa IntrusiveCounter.
324 *
325 * This is a counter class providing the ref() and unref() functions
326 * required by IntrusivePtr. It is intended to be inherited from by
327 * classes which are to be managed by such a smart pointer, and
328 * includes synchronization so that such an inheriting class object
329 * can be accessed by different IntrusivePtr objects in different
330 * threads (although the word Lock is in the title, by default it uses
331 * glib atomic functions to access the reference count rather than a
332 * mutex, so the overhead should be very small). Note that only the
333 * reference count is protected, so this is thread safe in the sense
334 * in which a raw pointer is thread safe.
335 *
336 * As mentioned, by default glib atomic functions are used to provide
337 * thread-safe manipulation of the reference count. However, the
338 * symbol CGU_INTRUSIVE_LOCK_COUNTER_USE_MUTEX can be defined so that
339 * the library uses mutexes instead, which might be useful for some
340 * debugging purposes. Note that if
341 * CGU_INTRUSIVE_LOCK_COUNTER_USE_MUTEX is to be defined, this is best
342 * done by textually amending the intrusive_ptr.h header file before
343 * the library is compiled. This will ensure that everything in the
344 * program and the library which includes the intrusive_ptr.h header
345 * is guaranteed to see the same definitions so that the C++
346 * standard's one-definition-rule is complied with.
347 */
348
349 class IntrusiveLockCounter {
350 #ifdef CGU_INTRUSIVE_LOCK_COUNTER_USE_MUTEX
351 unsigned int count;
352 Thread::Mutex mutex;
353 #else
354 gint count;
355 #endif
356
357 public:
358 // we should not be able to copy objects of this class
359 // - objects, if constructed on the heap, should be passed
360 // via an IntrusivePtr object. An object of a derived class
361 // might still copy itself via its copy constructor and take
362 // along a new base IntrusiveLockCounter object constructed via
363 // the default constructor, and thus have a correct reference
364 // count of 0, but derived classes should not try to provide
365 // their own assignment operators.
366 /**
367 * This class cannot be copied. The copy constructor is deleted.
368 */
369 IntrusiveLockCounter(const IntrusiveLockCounter&) = delete;
370 /**
371 * This class cannot be copied. The assignment operator is deleted.
372 */
373 IntrusiveLockCounter& operator=(const IntrusiveLockCounter&) = delete;
374
375 /**
376 * Increments the reference count. This method does not throw.
377 */
378 void ref() noexcept {
379 #ifdef CGU_INTRUSIVE_LOCK_COUNTER_USE_MUTEX
380 Thread::Mutex::Lock lock(mutex);
381 ++count;
382 #else
383 g_atomic_int_inc(&count);
384 #endif
385 }
386
387 /**
388 * Decrements the reference count, and if the count reaches 0 deletes
389 * itself (ie the managed object). This method does not throw unless
390 * the destructor of a derived class throws - that should never
391 * happen.
392 */
393 void unref() {
394 #ifdef CGU_INTRUSIVE_LOCK_COUNTER_USE_MUTEX
395 mutex.lock();
396 --count;
397 if (count == 0) {
398 mutex.unlock();
399 delete this;
400 }
401 else mutex.unlock();
402 #else
403 if (g_atomic_int_dec_and_test(&count)) {
404 delete this;
405 }
406 #endif
407 }
408
409 /**
410 * By default, glib atomic functions are used to provide thread-safe
411 * manipulation of the reference count. However, the header file
412 * intrusive_ptr.h can be textually amended before the library is
413 * compiled to define the symbol CGU_INTRUSIVE_LOCK_COUNTER_USE_MUTEX
414 * so as to use mutexes instead, which might be useful for some
415 * debugging purposes. Were that to be done, Cgu::Thread::MutexError
416 * might be thrown by this constructor if initialization of the mutex
417 * fails.
418 *
419 * Otherwise, this constructor does not throw.
420 */
421 IntrusiveLockCounter(): count(0) {}
422
423 /**
424 * This destructor does not throw, unless the destructor of a derived
425 * class throws - that should never happen.
426 */
427 virtual ~IntrusiveLockCounter() {}
428 };
429
430 #if defined(CGU_USE_SMART_PTR_COMPARISON) || defined(DOXYGEN_PARSING)
431
432 // we can use built-in operator == when comparing pointers referencing
433 // different objects of the same type
434 /**
435 * @ingroup handles
436 *
437 * This comparison operator does not throw. It compares the addresses
438 * of the managed objects.
439 *
440 * Since 2.0.0-rc2
441 */
442 template <class T>
443 bool operator==(const IntrusivePtr<T>& s1, const IntrusivePtr<T>& s2) noexcept {
444 return (s1.get() == s2.get());
445 }
446
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) noexcept {
457 return !(s1 == s2);
458 }
459
460 // we must use std::less rather than the < built-in operator for
461 // pointers to objects not within the same array or object: "For
462 // templates greater, less, greater_equal, and less_equal, the
463 // specializations for any pointer type yield a total order, even if
464 // the built-in operators <, >, <=, >= do not." (para 20.3.3/8).
465 /**
466 * @ingroup handles
467 *
468 * This comparison operator does not throw unless std::less applied to
469 * pointer types throws (which it would not do with any sane
470 * implementation). It compares the addresses of the managed objects.
471 *
472 * Since 2.0.0-rc2
473 */
474 template <class T>
475 bool operator<(const IntrusivePtr<T>& s1, const IntrusivePtr<T>& s2) {
476 return std::less<T*>()(s1.get(), s2.get());
477 }
478
479 #endif // CGU_USE_SMART_PTR_COMPARISON
480
481 } // namespace Cgu
482
483 // doxygen produces long filenames that tar can't handle:
484 // we have generic documentation for std::hash specialisations
485 // in doxygen.main.in
486 #if defined(CGU_USE_SMART_PTR_COMPARISON) && !defined(DOXYGEN_PARSING)
487 /* This struct allows InstrusivePtr objects to be keys in unordered
488 associative containers */
489 namespace std {
490 template <class T>
491 struct hash<Cgu::IntrusivePtr<T>> {
492 typedef std::size_t result_type;
493 typedef Cgu::IntrusivePtr<T> argument_type;
494 result_type operator()(const argument_type& s) const {
495 // this is fine: std::hash structs do not normally contain data and
496 // std::hash<T*> certainly won't, so we don't have overhead constructing
497 // std::hash<T*> on the fly
498 return std::hash<T*>()(s.get());
499 }
500 };
501 } // namespace std
502 #endif // CGU_USE_SMART_PTR_COMPARISON
503
504 #endif