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

c++-gtk-utils

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

- File List
- File Members

shared_ptr.h
Go to the documentation of this file.
1 /* Copyright (C) 2004 to 2014 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_SHARED_PTR_H
40 #define CGU_SHARED_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 // SharedLockPtr class
45 /* #define CGU_SHARED_LOCK_PTR_USE_MUTEX 1 */
46
47 #include <utility> // for std::move and std::swap
48 #include <exception>
49 #include <new>
50 #include <functional> // for std::less and std::hash<T*>
51 #include <cstddef> // for std::size_t
52
53 #ifdef CGU_SHARED_LOCK_PTR_USE_MUTEX
54 #include <c++-gtk-utils/mutex.h>
55 #else
56 #include <glib.h>
57 #endif
58
59 #include <c++-gtk-utils/cgu_config.h>
60
61 /**
62 * @addtogroup handles handles and smart pointers
63 */
64
65 namespace Cgu {
66
67 /**
68 * @class SharedPtrError shared_ptr.h c++-gtk-utils/shared_ptr.h
69 * @brief This is an exception struct thrown as an alternative to
70 * deleting a managed object when internal memory allocation for
71 * SharedPtr or SharedLockPtr fails in their reset() method or in
72 * their constructor which takes a pointer.
73 * @ingroup handles
74 * @sa SharedPtr SharedLockPtr SharedPtrAllocFail
75 *
76 * This is an exception struct thrown as an alternative to deleting a
77 * managed object when SharedPtr<T>::SharedPtr(T*),
78 * SharedLockPtr<T>::SharedLockPtr(T*), SharedPtr<T>::reset(T*) or
79 * SharedLockPtr<T>::reset(T*), would otherwise throw std::bad_alloc.
80 * To make those methods do that, Cgu::SharedPtrAllocFail::leave is
81 * passed as their second argument.
82 *
83 * If the exception is thrown, the struct has a member 'obj' of type
84 * T*, which is a pointer to the object originally passed to those
85 * methods, so the user can deal with it appropriately. This enables
86 * the result of the new expression to be passed directly as the
87 * argument to those methods without giving rise to a resource leak,
88 * as in:
89 *
90 * @code
91 * using namespace Cgu;
92 * SharedPtr<T> s; // doesn't throw
93 * try {
94 * s.reset(new T, SharedPtrAllocFail::leave); // both T allocation and reset() might throw
95 * }
96 * catch (std::bad_alloc&) {
97 * ...
98 * }
99 * catch (SharedPtrError<T>& e) {
100 * e.obj->do_something();
101 * ...
102 * }
103 * ...
104 * @endcode
105 *
106 * As above, a catch block will need to deal with std::bad_alloc (if
107 * the call to the new expression when creating the T object fails)
108 * as well as SharedPtrError (if the call to the new expression in
109 * the reset() method fails after a valid T object has been
110 * constructed).
111 */
112
113 template <class T> struct SharedPtrError: public std::exception {
114 T* obj;
115 virtual const char* what() const throw() {return "SharedPtrError\n";}
116 SharedPtrError(T* p): obj(p) {}
117 };
118
119 /**
120 * enum Cgu::SharedPtrAllocFail::Leave
121 * The enumerator Cgu::SharedPtrAllocFail::leave is passed as the
122 * second argument of the reset() method of SharedPtr or
123 * SharedLockPtr, or in their constructor which takes a pointer, in
124 * order to prevent the method deleting the object passed to it if
125 * reset() fails internally because of memory exhaustion.
126 * @ingroup handles
127 */
128 namespace SharedPtrAllocFail {
129 enum Leave {leave};
130 }
131
132
133 /**
134 * @class SharedPtr shared_ptr.h c++-gtk-utils/shared_ptr.h
135 * @brief This is a smart pointer for managing the lifetime of objects
136 * allocated on freestore.
137 * @ingroup handles
138 * @sa SharedLockPtr SharedPtrError
139 *
140 * This is a smart pointer for managing the lifetime of objects
141 * allocated on freestore with the new expression. A managed object
142 * will be deleted when the last SharedPtr referencing it is
143 * destroyed.
144 *
145 * @b Comparison @b with @b std::shared_ptr
146 *
147 * Most of the things that can be done by this class can be done by
148 * using std::shared_ptr from C++11/14, but this class is retained in
149 * the c++-gtk-utils library not only to retain compatibility with
150 * series 1.2 of the library, but also to cater for some cases not met
151 * (or not so easily met) by std::shared_ptr:
152 *
153 * 1. Glib memory slices provide an efficient small object allocator
154 * (they are likely to be significantly more efficient than global
155 * operator new()/new[](), which generally hand off to malloc(),
156 * and whilst malloc() is good for large block allocations it is
157 * generally poor as a small object allocator). Internal
158 * Cgu::SharedPtr allocation using glib memory slices can be
159 * achieved by compiling the library with the
160 * \--with-glib-memory-slices-no-compat configuration option.
161 * 2. If glib memory slices are not used (which do not throw),
162 * constructing a shared pointer for a new managed object (or
163 * calling reset() for a new managed object) might throw if
164 * internal allocation fails. Although by default the
165 * Cgu::SharedPtr implementation will delete the new managed object
166 * in such a case, it also provides an alternative constructor and
167 * reset() method which instead enable the new object to be
168 * accessed via the thrown exception object so that user code can
169 * decide what to do; std::shared_ptr deletes the new object in
170 * every case.
171 * 3. A user can explicitly state whether the shared pointer object is
172 * to have atomic increment and decrement-and-test with respect to
173 * the reference count so that the reference count is thread safe
174 * ('no' in the case of Cgu::SharedPtr, and 'yes' in the case of
175 * Cgu::SharedLockPtr). Using atomic functions is unnecessary if
176 * the managed object concerned is only addressed in one thread
177 * (and might cause unwanted cache flushing in certain
178 * circumstances). std::shared_ptr will generally always use
179 * atomic functions with respect to its reference count in a
180 * multi-threaded program.
181 *
182 * In favour of std::shared_ptr, it has an associated
183 * std::make_shared() factory function which will construct both the
184 * referenced object and the shared pointer's reference count within a
185 * single memory block when the first shared pointer managing a
186 * particular object is constructed. Cgu::SharedPtr and
187 * Cgu::SharedLockPtr always allocate these separately, but this is
188 * partly mitigated by the use of glib memory slices to allocate the
189 * reference count where the \--with-glib-memory-slices-no-compat
190 * configuration option is chosen.
191 *
192 * In addition, std::shared_ptr has an associated std::weak_ptr class,
193 * which Cgu::SharedPtr does not (there is a Cgu::GobjWeakHandle
194 * class, but that is cognate with Cgu::GobjHandle and is only usable
195 * with GObjects).
196 *
197 * If the library is compiled with the
198 * \--with-glib-memory-slices-no-compat configuration option, as
199 * mentioned Cgu::SharedPtr constructs its reference counting
200 * internals using glib memory slices. Although it is safe in a
201 * multi-threaded program if glib < 2.32 is installed to construct a
202 * static SharedPtr object in global namespace (that is, prior to
203 * g_thread_init() being called) by means of the default constructor
204 * and/or a pointer argument of NULL, it is not safe if constructed
205 * with a non-NULL pointer value. If glib >= 2.32 is installed,
206 * global objects with memory slices are safe in all
207 * circumstances. (Having said that, it would be highly unusual to
208 * have global SharedPtr objects.)
209 */
210
211 template <class T> class SharedPtr {
212
213 #ifndef DOXYGEN_PARSING
214 struct RefItems {
215 unsigned int* ref_count_p;
216 T* obj_p;
217 } ref_items;
218 #endif
219
220 void unreference() {
221 if (!ref_items.ref_count_p) return;
222 --(*ref_items.ref_count_p);
223 if (*ref_items.ref_count_p == 0) {
224 #ifdef CGU_USE_GLIB_MEMORY_SLICES_NO_COMPAT
225 g_slice_free(unsigned int, ref_items.ref_count_p);
226 #else
227 delete ref_items.ref_count_p;
228 #endif
229 delete ref_items.obj_p;
230 }
231 }
232
233 void reference() noexcept {
234 if (!ref_items.ref_count_p) return;
235 ++(*ref_items.ref_count_p);
236 }
237
238 public:
239 /**
240 * Constructor taking an unmanaged object.
241 * @param ptr The object which the SharedPtr is to manage (if any).
242 * @exception std::bad_alloc This constructor will not throw if the
243 * 'ptr' argument has a NULL value (the default), otherwise it might
244 * throw std::bad_alloc if memory is exhausted and the system throws
245 * in that case. If such an exception is thrown, this constructor is
246 * exception safe (it does not leak resources), but as well as
247 * cleaning itself up this constructor will also delete the managed
248 * object passed to it to avoid a memory leak. If such automatic
249 * deletion is not wanted in that case, use the version of this
250 * constructor taking a Cgu::SharedPtrAllocFail::Leave tag argument.
251 * @note std::bad_alloc will not be thrown if the library has been
252 * installed using the \--with-glib-memory-slices-no-compat
253 * configuration option: instead glib will terminate the program if it
254 * is unable to obtain memory from the operating system.
255 */
256 explicit SharedPtr(T* ptr = 0) {
257
258 if ((ref_items.obj_p = ptr)) { // not NULL
259 #ifdef CGU_USE_GLIB_MEMORY_SLICES_NO_COMPAT
260 ref_items.ref_count_p = g_slice_new(unsigned int);
261 *ref_items.ref_count_p = 1;
262 #else
263 try {
264 ref_items.ref_count_p = new unsigned int(1);
265 }
266 catch (...) {
267 delete ptr; // if allocating the int referenced by ref_items.ref_count_p
268 // has failed then delete the object to be referenced to
269 // avoid a memory leak
270 throw;
271 }
272 #endif
273 }
274 else ref_items.ref_count_p = 0;
275 }
276
277 /**
278 * Constructor taking an unmanaged object.
279 * @param ptr The object which the SharedPtr is to manage.
280 * @param tag Passing the tag emumerator
281 * Cgu::SharedPtrAllocFail::leave causes this constructor not to
282 * delete the new managed object passed as the 'ptr' argument in the
283 * event of internal allocation in this method failing because of
284 * memory exhaustion (in that event, Cgu::SharedPtrError will be
285 * thrown).
286 * @exception Cgu::SharedPtrError This constructor might throw
287 * Cgu::SharedPtrError if memory is exhausted and the system would
288 * otherwise throw std::bad_alloc in that case. This constructor is
289 * exception safe (it does not leak resources), and if such an
290 * exception is thrown it will clean itself up, but it will not
291 * attempt to delete the new managed object passed to it. Access to
292 * the object passed to the 'ptr' argument can be obtained via the
293 * thrown Cgu::SharedPtrError object.
294 * @note 1. On systems with over-commit/lazy-commit combined with
295 * virtual memory (swap), it is rarely useful to check for memory
296 * exhaustion, so in those cases this version of the constructor will
297 * not be useful.
298 * @note 2. If the library has been installed using the
299 * \--with-glib-memory-slices-no-compat configuration option this
300 * version of the constructor will also not be useful: instead glib
301 * will terminate the program if it is unable to obtain memory from
302 * the operating system.
303 */
304 SharedPtr(T* ptr, Cgu::SharedPtrAllocFail::Leave tag) {
305
306 if ((ref_items.obj_p = ptr)) { // not NULL
307 #ifdef CGU_USE_GLIB_MEMORY_SLICES_NO_COMPAT
308 ref_items.ref_count_p = g_slice_new(unsigned int);
309 *ref_items.ref_count_p = 1;
310 #else
311 try {
312 ref_items.ref_count_p = new unsigned int(1);
313 }
314 catch (std::bad_alloc&) { // as we are not rethrowing, make NPTL friendly
315 throw SharedPtrError<T>(ptr);
316 }
317 #endif
318 }
319 else ref_items.ref_count_p = 0;
320 }
321
322 /**
323 * Causes the SharedPtr to cease to manage its managed object (if
324 * any), deleting it if this is the last SharedPtr object managing it.
325 * If the argument passed is not NULL, the SharedPtr object will
326 * manage the new object passed (which must not be managed by any
327 * other SharedPtr object). This method is exception safe, but see
328 * the comments below on std::bad_alloc.
329 * @param ptr NULL (the default), or a new unmanaged object to manage.
330 * @exception std::bad_alloc This method will not throw if the 'ptr'
331 * argument has a NULL value (the default) and the destructor of a
332 * managed object does not throw, otherwise it might throw
333 * std::bad_alloc if memory is exhausted and the system throws in that
334 * case. Note that if such an exception is thrown then this method
335 * will do nothing (it is strongly exception safe and will continue to
336 * manage the object it was managing prior to the call), except that
337 * it will delete the new managed object passed to it to avoid a
338 * memory leak. If such automatic deletion in the event of such an
339 * exception is not wanted, use the reset() method taking a
340 * Cgu::SharedPtrAllocFail::Leave tag type as its second argument.
341 * @note std::bad_alloc will not be thrown if the library has been
342 * installed using the \--with-glib-memory-slices-no-compat
343 * configuration option: instead glib will terminate the program if it
344 * is unable to obtain memory from the operating system.
345 */
346 void reset(T* ptr = 0) {
347 SharedPtr tmp(ptr);
348 std::swap(ref_items, tmp.ref_items);
349 }
350
351 /**
352 * Causes the SharedPtr to cease to manage its managed object (if
353 * any), deleting it if this is the last SharedPtr object managing it.
354 * The SharedPtr object will manage the new object passed (which must
355 * not be managed by any other SharedPtr object). This method is
356 * exception safe, but see the comments below on Cgu::SharedPtrError.
357 * @param ptr A new unmanaged object to manage (if no new object is to
358 * be managed, use the version of reset() taking a default value of
359 * NULL).
360 * @param tag Passing the tag emumerator
361 * Cgu::SharedPtrAllocFail::leave causes this method not to delete the
362 * new managed object passed as the 'ptr' argument in the event of
363 * internal allocation in this method failing because of memory
364 * exhaustion (in that event, Cgu::SharedPtrError will be thrown).
365 * @exception Cgu::SharedPtrError This method might throw
366 * Cgu::SharedPtrError if memory is exhausted and the system would
367 * otherwise throw std::bad_alloc in that case. Note that if such an
368 * exception is thrown then this method will do nothing (it is
369 * strongly exception safe and will continue to manage the object it
370 * was managing prior to the call), and it will not attempt to delete
371 * the new managed object passed to it. Access to the object passed
372 * to the 'ptr' argument can be obtained via the thrown
373 * Cgu::SharedPtrError object.
374 * @note 1. On systems with over-commit/lazy-commit combined with
375 * virtual memory (swap), it is rarely useful to check for memory
376 * exhaustion, so in those cases this version of the reset() method
377 * will not be useful.
378 * @note 2. If the library has been installed using the
379 * \--with-glib-memory-slices-no-compat configuration option this
380 * version of the reset() method will also not be useful: instead glib
381 * will terminate the program if it is unable to obtain memory from
382 * the operating system.
383 */
384 void reset(T* ptr, Cgu::SharedPtrAllocFail::Leave tag) {
385 SharedPtr tmp(ptr, tag);
386 std::swap(ref_items, tmp.ref_items);
387 }
388
389 /**
390 * This copy constructor does not throw.
391 * @param sh_ptr The shared pointer to be copied.
392 */
393 SharedPtr(const SharedPtr& sh_ptr) noexcept {
394 ref_items = sh_ptr.ref_items;
395 reference();
396 }
397
398 /**
399 * The move constructor does not throw. It has move semantics.
400 * @param sh_ptr The shared pointer to be moved.
401 */
402 SharedPtr(SharedPtr&& sh_ptr) noexcept {
403 ref_items = sh_ptr.ref_items;
404 sh_ptr.ref_items.ref_count_p = 0;
405 sh_ptr.ref_items.obj_p = 0;
406 }
407
408 template <class U> friend class SharedPtr;
409
410 /**
411 * A version of the copy constructor which enables pointer type
412 * conversion (assuming the type passed is implicitly type
413 * convertible to the managed type, such as a derived type). This
414 * copy constructor does not throw.
415 * @param sh_ptr The shared pointer to be copied.
416 */
417 template <class U> SharedPtr(const SharedPtr<U>& sh_ptr) noexcept {
418 // because we are allowing an implicit cast from derived to
419 // base class referenced object, we need to assign from each
420 // member of sh_ptr.ref_items separately
421 ref_items.ref_count_p = sh_ptr.ref_items.ref_count_p;
422 ref_items.obj_p = sh_ptr.ref_items.obj_p;
423 reference();
424 }
425
426 /**
427 * A version of the move constructor which enables pointer type
428 * conversion (assuming the type passed is implicitly type
429 * convertible to the managed type, such as a derived type). This
430 * move constructor does not throw.
431 * @param sh_ptr The shared pointer to be moved.
432 */
433 template <class U> SharedPtr(SharedPtr<U>&& sh_ptr) noexcept {
434 // because we are allowing an implicit cast from derived to
435 // base class referenced object, we need to assign from each
436 // member of sh_ptr.ref_items separately
437 ref_items.ref_count_p = sh_ptr.ref_items.ref_count_p;
438 ref_items.obj_p = sh_ptr.ref_items.obj_p;
439 sh_ptr.ref_items.ref_count_p = 0;
440 sh_ptr.ref_items.obj_p = 0;
441 }
442
443 /**
444 * This method (and so copy or move assignment) does not throw unless
445 * the destructor of a managed object throws.
446 * @param sh_ptr the assignor.
447 * @return The SharedPtr object after assignment.
448 */
449 // having a value type as the argument, rather than reference to const
450 // and then initialising a tmp object, gives the compiler more scope
451 // for optimisation, and also caters for r-values without a separate
452 // overload
453 SharedPtr& operator=(SharedPtr sh_ptr) {
454 std::swap(ref_items, sh_ptr.ref_items);
455 return *this;
456 }
457
458 /**
459 * A version of the assignment operator which enables pointer type
460 * conversion (assuming the type passed is implicitly type
461 * convertible to the managed type, such as a derived type). This
462 * method does not throw unless the destructor of a managed object
463 * throws.
464 * @param sh_ptr the assignor.
465 * @return The SharedPtr object after assignment.
466 */
467 template <class U> SharedPtr& operator=(const SharedPtr<U>& sh_ptr) {
468 return operator=(SharedPtr(sh_ptr));
469 }
470
471 /**
472 * A version of the operator for move assignment which enables
473 * pointer type conversion (assuming the type passed is implicitly
474 * type convertible to the managed type, such as a derived type).
475 * This method does not throw unless the destructor of a managed
476 * object throws.
477 * @param sh_ptr the shared pointer to be moved.
478 * @return The SharedPtr object after the move operation.
479 */
480 template <class U> SharedPtr& operator=(SharedPtr<U>&& sh_ptr) {
481 return operator=(SharedPtr(std::move(sh_ptr)));
482 }
483
484 /**
485 * This method does not throw.
486 * @return A pointer to the managed object (or NULL if none is
487 * managed).
488 */
489 T* get() const noexcept {return ref_items.obj_p;}
490
491 /**
492 * This method does not throw.
493 * @return A reference to the managed object.
494 */
495 T& operator*() const noexcept {return *ref_items.obj_p;}
496
497 /**
498 * This method does not throw.
499 * @return A pointer to the managed object (or NULL if none is
500 * managed).
501 */
502 T* operator->() const noexcept {return ref_items.obj_p;}
503
504 /**
505 * This method does not throw.
506 * @return The number of SharedPtr objects referencing the managed
507 * object (or 0 if none is managed by this SharedPtr).
508 */
509 unsigned int get_refcount() const noexcept {return (ref_items.ref_count_p) ? *ref_items.ref_count_p : 0;}
510
511 /**
512 * The destructor does not throw unless the destructor of a managed
513 * object throws - that should never happen.
514 */
515 ~SharedPtr() {unreference();}
516 };
517
518 /**
519 * @class SharedLockPtr shared_ptr.h c++-gtk-utils/shared_ptr.h
520 * @brief This is a smart pointer for managing the lifetime of objects
521 * allocated on freestore, with a thread safe reference count.
522 * @ingroup handles
523 * @sa SharedPtr SharedPtrError
524 *
525 * Class SharedLockPtr is a version of the shared pointer class which
526 * includes synchronization so that it can handle objects accessed in
527 * multiple threads (although the word Lock is in the title, by
528 * default it uses glib atomic functions to access the reference count
529 * rather than a mutex, so the overhead should be very small). Note
530 * that only the reference count is protected, so this is thread safe
531 * in the sense in which a raw pointer is thread safe. A shared
532 * pointer accessed in one thread referencing a particular object is
533 * thread safe as against another shared pointer accessing the same
534 * object in a different thread. It is thus suitable for use in
535 * different standard C++ containers which exist in different threads
536 * but which contain shared objects by reference. But:
537 *
538 * 1. If the referenced object is to be modified in one thread and
539 * read or modified in another thread an appropriate mutex for the
540 * referenced object is required (unless that referenced object
541 * does its own locking).
542 * 2. If the same instance of shared pointer is to be modified in one
543 * thread (by assigning to the pointer so that it references a
544 * different object, or by moving from it), and copied (assigned
545 * from or used as the argument of a copy constructor), accessed,
546 * destroyed or modified in another thread, a mutex for that
547 * instance of shared pointer is required.
548 * 3. Objects referenced by shared pointers which are objects for
549 * which POSIX provides no guarantees (in the main, those which
550 * are not built-in types), such as strings and similar
551 * containers, may not support concurrent reads in different
552 * threads. That depends on the library implementation concerned.
553 * If that is the case, a mutex for the referenced object will
554 * also be required when reading any given instance of such an
555 * object in more than one thread by dereferencing any shared
556 * pointers referencing it (and indeed, when not using shared
557 * pointers at all).
558 *
559 * As mentioned, by default glib atomic functions are used to provide
560 * thread-safe manipulation of the reference count. However, the
561 * symbol CGU_SHARED_LOCK_PTR_USE_MUTEX can be defined so that the
562 * library uses mutexes instead, which might be useful for some
563 * debugging purposes. Note that if CGU_SHARED_LOCK_PTR_USE_MUTEX is
564 * to be defined, this is best done by textually amending the
565 * shared_ptr.h header file before the library is compiled. This will
566 * ensure that everything in the program and the library which
567 * includes the shared_ptr.h header is guaranteed to see the same
568 * definitions so that the C++ standard's one-definition-rule is
569 * complied with.
570 *
571 * @b Comparison @b with @b std::shared_ptr
572 *
573 * Most of the things that can be done by this class can be done by
574 * using std::shared_ptr from C++11/14, but this class is retained in
575 * the c++-gtk-utils library not only to retain compatibility with
576 * series 1.2 of the library, but also to cater for some cases not met
577 * (or not so easily met) by std::shared_ptr:
578 *
579 * 1. Glib memory slices provide an efficient small object allocator
580 * (they are likely to be significantly more efficient than global
581 * operator new()/new[](), which generally hand off to malloc(),
582 * and whilst malloc() is good for large block allocations it is
583 * generally poor as a small object allocator). Internal
584 * Cgu::SharedLockPtr allocation using glib memory slices can be
585 * achieved by compiling the library with the
586 * \--with-glib-memory-slices-no-compat configuration option.
587 * 2. If glib memory slices are not used (which do not throw),
588 * constructing a shared pointer for a new managed object (or
589 * calling reset() for a new managed object) might throw if
590 * internal allocation fails. Although by default the
591 * Cgu::SharedLockPtr implementation will delete the new managed
592 * object in such a case, it also provides an alternative
593 * constructor and reset() method which instead enable the new
594 * object to be accessed via the thrown exception object so that
595 * user code can decide what to do; std::shared_ptr deletes the new
596 * object in every case.
597 * 3. A user can explicitly state whether the shared pointer object is
598 * to have atomic increment and decrement-and-test with respect to
599 * the reference count so that the reference count is thread safe
600 * ('no' in the case of Cgu::SharedPtr, and 'yes' in the case of
601 * Cgu::SharedLockPtr). Using atomic functions is unnecessary if
602 * the managed object concerned is only addressed in one thread
603 * (and might cause unwanted cache flushing in certain
604 * circumstances). std::shared_ptr will generally always use
605 * atomic functions with respect to its reference count in a
606 * multi-threaded program.
607 *
608 * In favour of std::shared_ptr, it has an associated
609 * std::make_shared() factory function which will construct both the
610 * referenced object and the shared pointer's reference count within a
611 * single memory block when the first shared pointer managing a
612 * particular object is constructed. Cgu::SharedPtr and
613 * Cgu::SharedLockPtr always allocate these separately, but this is
614 * partly mitigated by the use of glib memory slices to allocate the
615 * reference count where the \--with-glib-memory-slices-no-compat
616 * configuration option is chosen.
617 *
618 * In addition, std::shared_ptr has an associated std::weak_ptr class,
619 * which Cgu::SharedLockPtr does not (there is a Cgu::GobjWeakHandle
620 * class, but that is cognate with Cgu::GobjHandle and is only usable
621 * with GObjects), and shared_ptr objects also have some atomic store,
622 * load and exchange functions provided for them which enable
623 * concurrent modifications of the same instance of shared_ptr in
624 * different threads to have defined results.
625 *
626 * If the library is compiled with the
627 * \--with-glib-memory-slices-no-compat configuration option, as
628 * mentioned Cgu::SharedLockPtr constructs its reference counting
629 * internals using glib memory slices. Although it is safe in a
630 * multi-threaded program if glib < 2.32 is installed to construct a
631 * static SharedLockPtr object in global namespace (that is, prior to
632 * g_thread_init() being called) by means of the default constructor
633 * and/or a pointer argument of NULL, it is not safe if constructed
634 * with a non-NULL pointer value. If glib >= 2.32 is installed,
635 * global objects with memory slices are safe in all
636 * circumstances. (Having said that, it would be highly unusual to
637 * have global SharedLockPtr objects.)
638 */
639
640 template <class T> class SharedLockPtr {
641
642 #ifndef DOXYGEN_PARSING
643 struct RefItems {
644 #ifdef CGU_SHARED_LOCK_PTR_USE_MUTEX
645 Thread::Mutex* mutex_p;
646 unsigned int* ref_count_p;
647 #else
648 gint* ref_count_p;
649 #endif
650 T* obj_p;
651 } ref_items;
652 #endif
653
654 // SharedLockPtr<T>::unreference() does not throw if the destructor of the
655 // contained object does not throw, because Thread::Mutex::~Mutex(),
656 // Thread::Mutex::lock() and Thread::Mutex::unlock() do not throw
657 void unreference() {
658 // we can (and should) check whether ref_items.ref_count_p is NULL without
659 // a lock, because that member is specific to this SharedLockPtr object.
660 // Only the integer pointed to by it is shared amongst SharedLockPtr
661 // objects and requires locking
662 if (!ref_items.ref_count_p) return;
663 #ifdef CGU_SHARED_LOCK_PTR_USE_MUTEX
664 ref_items.mutex_p->lock();
665 --(*ref_items.ref_count_p);
666 if (*ref_items.ref_count_p == 0) {
667 # ifdef CGU_USE_GLIB_MEMORY_SLICES_NO_COMPAT
668 g_slice_free(unsigned int, ref_items.ref_count_p);
669 # else
670 delete ref_items.ref_count_p;
671 # endif
672 ref_items.mutex_p->unlock();
673 delete ref_items.mutex_p;
674 delete ref_items.obj_p;
675 }
676 else ref_items.mutex_p->unlock();
677 #else
678 if (g_atomic_int_dec_and_test(ref_items.ref_count_p)) {
679 # ifdef CGU_USE_GLIB_MEMORY_SLICES_NO_COMPAT
680 g_slice_free(gint, ref_items.ref_count_p);
681 # else
682 delete ref_items.ref_count_p;
683 # endif
684 delete ref_items.obj_p;
685 }
686 #endif
687 }
688
689 // SharedLockPtr<T>::reference() does not throw because
690 // Thread::Mutex::Lock::Lock() and Thread::Mutex::Lock::~Lock() do not throw
691 void reference() noexcept {
692 // we can (and should) check whether ref_items.ref_count_p is NULL without
693 // a lock, because that member is specific to this SharedLockPtr object.
694 // Only the integer pointed to by it is shared amongst SharedLockPtr
695 // objects and requires locking
696 if (!ref_items.ref_count_p) return;
697 #ifdef CGU_SHARED_LOCK_PTR_USE_MUTEX
698 Thread::Mutex::Lock lock(*ref_items.mutex_p);
699 ++(*ref_items.ref_count_p);
700 #else
701 g_atomic_int_inc(ref_items.ref_count_p);
702 #endif
703 }
704
705 public:
706 /**
707 * Constructor taking an unmanaged object.
708 * @param ptr The object which the SharedLockPtr is to manage (if
709 * any).
710 * @exception std::bad_alloc This constructor will not throw if the
711 * 'ptr' argument has a NULL value (the default), otherwise it might
712 * throw std::bad_alloc if memory is exhausted and the system throws
713 * in that case. If such an exception is thrown, this constructor is
714 * exception safe (it does not leak resources), but as well as
715 * cleaning itself up this constructor will also delete the managed
716 * object passed to it to avoid a memory leak. If such automatic
717 * deletion is not wanted in that case, use the version of this
718 * constructor taking a Cgu::SharedPtrAllocFail::Leave tag argument.
719 * @note 1. std::bad_alloc will not be thrown if the library has been
720 * installed using the \--with-glib-memory-slices-no-compat
721 * configuration option: instead glib will terminate the program if it
722 * is unable to obtain memory from the operating system.
723 * @note 2. By default, glib atomic functions are used to provide
724 * thread-safe manipulation of the reference count. However, the
725 * header file shared_ptr.h can be textually amended before the
726 * library is compiled to define the symbol
727 * CGU_SHARED_LOCK_PTR_USE_MUTEX so as to use mutexes instead, which
728 * might be useful for some debugging purposes. Were that to be done,
729 * Cgu::Thread::MutexError might be thrown by this constructor if
730 * initialization of the mutex fails.
731 */
732 explicit SharedLockPtr(T* ptr = 0) {
733
734 if ((ref_items.obj_p = ptr)) { // not NULL
735 #ifdef CGU_SHARED_LOCK_PTR_USE_MUTEX
736 try {
737 ref_items.mutex_p = new Thread::Mutex;
738 }
739 catch (...) {
740 delete ptr; // if allocating the object referenced by ref_items.mutex_p
741 // has failed then delete the object to be referenced to
742 // avoid a memory leak
743 throw;
744 }
745 # ifdef CGU_USE_GLIB_MEMORY_SLICES_NO_COMPAT
746 ref_items.ref_count_p = g_slice_new(unsigned int);
747 *ref_items.ref_count_p = 1;
748 # else
749 try {
750 ref_items.ref_count_p = new unsigned int(1);
751 }
752 catch (...) {
753 delete ref_items.mutex_p;
754 delete ptr;
755 throw;
756 }
757 # endif
758 #else
759 # ifdef CGU_USE_GLIB_MEMORY_SLICES_NO_COMPAT
760 ref_items.ref_count_p = g_slice_new(gint);
761 *ref_items.ref_count_p = 1;
762 # else
763 try {
764 ref_items.ref_count_p = new gint(1);
765 }
766 catch (...) {
767 delete ptr; // if allocating the int referenced by ref_items.ref_count_p
768 // has failed then delete the object to be referenced to
769 // avoid a memory leak
770 throw;
771 }
772 # endif
773 #endif
774 }
775 else {
776 #ifdef CGU_SHARED_LOCK_PTR_USE_MUTEX
777 ref_items.mutex_p = 0; // make sure the value is valid as we may assign it
778 #endif
779 ref_items.ref_count_p = 0;
780 }
781 }
782
783 /**
784 * Constructor taking an unmanaged object.
785 * @param ptr The object which the SharedLockPtr is to manage.
786 * @param tag Passing the tag emumerator
787 * Cgu::SharedPtrAllocFail::leave causes this constructor not to
788 * delete the new managed object passed as the 'ptr' argument in the
789 * event of internal allocation in this method failing because of
790 * memory exhaustion (in that event, Cgu::SharedPtrError will be
791 * thrown).
792 * @exception Cgu::SharedPtrError This constructor might throw
793 * Cgu::SharedPtrError if memory is exhausted and the system would
794 * otherwise throw std::bad_alloc in that case. This constructor is
795 * exception safe (it does not leak resources), and if such an
796 * exception is thrown it will clean itself up, but it will not
797 * attempt to delete the new managed object passed to it. Access to
798 * the object passed to the 'ptr' argument can be obtained via the
799 * thrown Cgu::SharedPtrError object.
800 * @note 1. On systems with over-commit/lazy-commit combined with
801 * virtual memory (swap), it is rarely useful to check for memory
802 * exhaustion, so in those cases this version of the constructor will
803 * not be useful.
804 * @note 2. If the library has been installed using the
805 * \--with-glib-memory-slices-no-compat configuration option this
806 * version of the constructor will also not be useful: instead glib
807 * will terminate the program if it is unable to obtain memory from
808 * the operating system.
809 * @note 3. By default, glib atomic functions are used to provide
810 * thread-safe manipulation of the reference count. However, the
811 * header file shared_ptr.h can be textually amended before the
812 * library is compiled to define the symbol
813 * CGU_SHARED_LOCK_PTR_USE_MUTEX so as to use mutexes instead, which
814 * might be useful for some debugging purposes. Were that to be done,
815 * Cgu::SharedPtrError might be thrown by this constructor if
816 * initialization of the mutex fails (even if the
817 * \--with-glib-memory-slices-no-compat configuration option is
818 * chosen).
819 */
820 SharedLockPtr(T* ptr, Cgu::SharedPtrAllocFail::Leave tag) {
821
822 if ((ref_items.obj_p = ptr)) { // not NULL
823 #ifdef CGU_SHARED_LOCK_PTR_USE_MUTEX
824 try {
825 ref_items.mutex_p = new Thread::Mutex;
826 }
827 catch (std::bad_alloc&) { // as we are not rethrowing, make NPTL friendly
828 throw SharedPtrError<T>(ptr);
829 }
830 catch (Thread::MutexError&) { // as we are not rethrowing, make NPTL friendly
831 throw SharedPtrError<T>(ptr);
832 }
833 # ifdef CGU_USE_GLIB_MEMORY_SLICES_NO_COMPAT
834 ref_items.ref_count_p = g_slice_new(unsigned int);
835 *ref_items.ref_count_p = 1;
836 # else
837 try {
838 ref_items.ref_count_p = new unsigned int(1);
839 }
840 catch (std::bad_alloc&) {
841 delete ref_items.mutex_p;
842 throw SharedPtrError<T>(ptr);
843 }
844 # endif
845 #else
846 # ifdef CGU_USE_GLIB_MEMORY_SLICES_NO_COMPAT
847 ref_items.ref_count_p = g_slice_new(gint);
848 *ref_items.ref_count_p = 1;
849 # else
850 try {
851 ref_items.ref_count_p = new gint(1);
852 }
853 catch (std::bad_alloc&) { // as we are not rethrowing, make NPTL friendly
854 throw SharedPtrError<T>(ptr);
855 }
856 # endif
857 #endif
858 }
859 else {
860 #ifdef CGU_SHARED_LOCK_PTR_USE_MUTEX
861 ref_items.mutex_p = 0; // make sure the value is valid as we may assign it
862 #endif
863 ref_items.ref_count_p = 0;
864 }
865 }
866
867 /**
868 * Causes the SharedLockPtr to cease to manage its managed object (if
869 * any), deleting it if this is the last SharedLockPtr object managing
870 * it. If the argument passed is not NULL, the SharedLockPtr object
871 * will manage the new object passed (which must not be managed by any
872 * other SharedLockPtr object). This method is exception safe, but
873 * see the comments below on std::bad_alloc.
874 * @param ptr NULL (the default), or a new unmanaged object to manage.
875 * @exception std::bad_alloc This method will not throw if the 'ptr'
876 * argument has a NULL value (the default) and the destructor of a
877 * managed object does not throw, otherwise it might throw
878 * std::bad_alloc if memory is exhausted and the system throws in that
879 * case. Note that if such an exception is thrown then this method
880 * will do nothing (it is strongly exception safe and will continue to
881 * manage the object it was managing prior to the call), except that
882 * it will delete the new managed object passed to it to avoid a
883 * memory leak. If such automatic deletion in the event of such an
884 * exception is not wanted, use the reset() method taking a
885 * Cgu::SharedPtrAllocFail::Leave tag type as its second argument.
886 * @note 1. std::bad_alloc will not be thrown if the library has been
887 * installed using the \--with-glib-memory-slices-no-compat
888 * configuration option: instead glib will terminate the program if it
889 * is unable to obtain memory from the operating system.
890 * @note 2. By default, glib atomic functions are used to provide
891 * thread-safe manipulation of the reference count. However, the
892 * header file shared_ptr.h can be textually amended before the
893 * library is compiled to define the symbol
894 * CGU_SHARED_LOCK_PTR_USE_MUTEX so as to use mutexes instead, which
895 * might be useful for some debugging purposes. Were that to be done,
896 * Cgu::Thread::MutexError might be thrown by this method if
897 * initialization of the mutex fails.
898 * @note 3. A SharedLockPtr object protects its reference count but
899 * not the managed object or its other internals. The reset() method
900 * should not be called by one thread in respect of a particular
901 * SharedLockPtr object while another thread may be operating on,
902 * copying or dereferencing the same instance of SharedLockPtr. It is
903 * thread-safe as against another instance of SharedLockPtr managing
904 * the same object.
905 */
906 void reset(T* ptr = 0) {
907 SharedLockPtr tmp(ptr);
908 std::swap(ref_items, tmp.ref_items);
909 }
910
911 /**
912 * Causes the SharedLockPtr to cease to manage its managed object (if
913 * any), deleting it if this is the last SharedLockPtr object managing
914 * it. The SharedLockPtr object will manage the new object passed
915 * (which must not be managed by any other SharedLockPtr object).
916 * This method is exception safe, but see the comments below on
917 * Cgu::SharedPtrError.
918 * @param ptr A new unmanaged object to manage (if no new object is to
919 * be managed, use the version of reset() taking a default value of
920 * NULL).
921 * @param tag Passing the tag emumerator
922 * Cgu::SharedPtrAllocFail::leave causes this method not to delete the
923 * new managed object passed as the 'ptr' argument in the event of
924 * internal allocation in this method failing because of memory
925 * exhaustion (in that event, Cgu::SharedPtrError will be thrown).
926 * @exception Cgu::SharedPtrError This method might throw
927 * Cgu::SharedPtrError if memory is exhausted and the system would
928 * otherwise throw std::bad_alloc in that case. Note that if such an
929 * exception is thrown then this method will do nothing (it is
930 * strongly exception safe and will continue to manage the object it
931 * was managing prior to the call), and it will not attempt to delete
932 * the new managed object passed to it. Access to the object passed
933 * to the 'ptr' argument can be obtained via the thrown
934 * Cgu::SharedPtrError object.
935 * @note 1. A SharedLockPtr object protects its reference count but
936 * not the managed object or its other internals. The reset() method
937 * should not be called by one thread in respect of a particular
938 * SharedLockPtr object while another thread may be operating on,
939 * copying or dereferencing the same instance of SharedLockPtr. It is
940 * thread-safe as against another instance of SharedLockPtr managing
941 * the same object.
942 * @note 2. On systems with over-commit/lazy-commit combined with
943 * virtual memory (swap), it is rarely useful to check for memory
944 * exhaustion, so in those cases this version of the reset() method
945 * will not be useful.
946 * @note 3. If the library has been installed using the
947 * \--with-glib-memory-slices-no-compat configuration option this
948 * version of the reset() method will also not be useful: instead glib
949 * will terminate the program if it is unable to obtain memory from
950 * the operating system.
951 * @note 4. By default, glib atomic functions are used to provide
952 * thread-safe manipulation of the reference count. However, the
953 * header file shared_ptr.h can be textually amended before the
954 * library is compiled to define the symbol
955 * CGU_SHARED_LOCK_PTR_USE_MUTEX so as to use mutexes instead, which
956 * might be useful for some debugging purposes. Were that to be done,
957 * Cgu::SharedPtrError might be thrown by this method if
958 * initialization of the mutex fails (even if the
959 * \--with-glib-memory-slices-no-compat configuration option is
960 * chosen).
961 */
962 void reset(T* ptr, Cgu::SharedPtrAllocFail::Leave tag) {
963 SharedLockPtr tmp(ptr, tag);
964 std::swap(ref_items, tmp.ref_items);
965 }
966
967 /**
968 * This copy constructor does not throw.
969 * @param sh_ptr The shared pointer to be copied.
970 */
971 SharedLockPtr(const SharedLockPtr& sh_ptr) noexcept {
972 ref_items = sh_ptr.ref_items;
973 reference();
974 }
975
976 /**
977 * The move constructor does not throw. It has move semantics.
978 * @param sh_ptr The shared pointer to be moved.
979 */
980 SharedLockPtr(SharedLockPtr&& sh_ptr) noexcept {
981 ref_items = sh_ptr.ref_items;
982 #ifdef CGU_SHARED_LOCK_PTR_USE_MUTEX
983 sh_ptr.ref_items.mutex_p = 0; // make sure the value is valid as we may assign it
984 #endif
985 sh_ptr.ref_items.ref_count_p = 0;
986 sh_ptr.ref_items.obj_p = 0;
987 }
988
989 template <class U> friend class SharedLockPtr;
990
991 /**
992 * A version of the copy constructor which enables pointer type
993 * conversion (assuming the type passed is implicitly type
994 * convertible to the managed type, such as a derived type). This
995 * copy constructor does not throw.
996 * @param sh_ptr The shared pointer to be copied.
997 */
998 template <class U> SharedLockPtr(const SharedLockPtr<U>& sh_ptr) noexcept {
999 // because we are allowing an implicit cast from derived to
1000 // base class referenced object, we need to assign from each
1001 // member of sh_ptr.ref_items separately
1002 #ifdef CGU_SHARED_LOCK_PTR_USE_MUTEX
1003 ref_items.mutex_p = sh_ptr.ref_items.mutex_p;
1004 #endif
1005 ref_items.ref_count_p = sh_ptr.ref_items.ref_count_p;
1006 ref_items.obj_p = sh_ptr.ref_items.obj_p;
1007 reference();
1008 }
1009
1010 /**
1011 * A version of the move constructor which enables pointer type
1012 * conversion (assuming the type passed is implicitly type
1013 * convertible to the managed type, such as a derived type). This
1014 * move constructor does not throw.
1015 * @param sh_ptr The shared pointer to be moved.
1016 */
1017 template <class U> SharedLockPtr(SharedLockPtr<U>&& sh_ptr) noexcept {
1018 // because we are allowing an implicit cast from derived to
1019 // base class referenced object, we need to assign from each
1020 // member of sh_ptr.ref_items separately
1021 #ifdef CGU_SHARED_LOCK_PTR_USE_MUTEX
1022 ref_items.mutex_p = sh_ptr.ref_items.mutex_p;
1023 #endif
1024 ref_items.ref_count_p = sh_ptr.ref_items.ref_count_p;
1025 ref_items.obj_p = sh_ptr.ref_items.obj_p;
1026
1027 #ifdef CGU_SHARED_LOCK_PTR_USE_MUTEX
1028 sh_ptr.ref_items.mutex_p = 0; // make sure the value is valid as we may assign it
1029 #endif
1030 sh_ptr.ref_items.ref_count_p = 0;
1031 sh_ptr.ref_items.obj_p = 0;
1032 }
1033
1034 /**
1035 * This method (and so copy or move assignment) does not throw unless
1036 * the destructor of a managed object throws.
1037 * @param sh_ptr the assignor.
1038 * @return The SharedLockPtr object after assignment.
1039 */
1040 // having a value type as the argument, rather than reference to const
1041 // and then initialising a tmp object, gives the compiler more scope
1042 // for optimisation, and also caters for r-values without a separate
1043 // overload
1044 SharedLockPtr& operator=(SharedLockPtr sh_ptr) {
1045 std::swap(ref_items, sh_ptr.ref_items);
1046 return *this;
1047 }
1048
1049 /**
1050 * A version of the assignment operator which enables pointer type
1051 * conversion (assuming the type passed is implicitly type
1052 * convertible to the managed type, such as a derived type). This
1053 * method does not throw unless the destructor of a managed object
1054 * throws.
1055 * @param sh_ptr the assignor.
1056 * @return The SharedLockPtr object after assignment.
1057 */
1058 template <class U> SharedLockPtr& operator=(const SharedLockPtr<U>& sh_ptr) {
1059 return operator=(SharedLockPtr(sh_ptr));
1060 }
1061
1062 /**
1063 * A version of the operator for move assignment which enables
1064 * pointer type conversion (assuming the type passed is implicitly
1065 * type convertible to the managed type, such as a derived type).
1066 * This method does not throw unless the destructor of a managed
1067 * object throws.
1068 * @param sh_ptr the shared pointer to be moved.
1069 * @return The SharedLockPtr object after the move operation.
1070 */
1071 template <class U> SharedLockPtr& operator=(SharedLockPtr<U>&& sh_ptr) {
1072 return operator=(SharedLockPtr(std::move(sh_ptr)));
1073 }
1074
1075 /**
1076 * This method does not throw.
1077 * @return A pointer to the managed object (or NULL if none is
1078 * managed).
1079 */
1080 T* get() const noexcept {return ref_items.obj_p;}
1081
1082 /**
1083 * This method does not throw.
1084 * @return A reference to the managed object.
1085 */
1086 T& operator*() const noexcept {return *ref_items.obj_p;}
1087
1088 /**
1089 * This method does not throw.
1090 * @return A pointer to the managed object (or NULL if none is
1091 * managed).
1092 */
1093 T* operator->() const noexcept {return ref_items.obj_p;}
1094
1095 /**
1096 * This method does not throw.
1097 * @return The number of SharedLockPtr objects referencing the
1098 * managed object (or 0 if none is managed by this SharedLockPtr).
1099 * @note The return value may not be valid if another thread has
1100 * changed the reference count before the value returned by this
1101 * method is acted on. It is provided as a utility, but may not be
1102 * meaningful, depending on the intended usage.
1103 */
1104 unsigned int get_refcount() const noexcept {
1105 if (!ref_items.ref_count_p) return 0;
1106 #ifdef CGU_SHARED_LOCK_PTR_USE_MUTEX
1107 Thread::Mutex::Lock lock(*ref_items.mutex_p);
1108 return *ref_items.ref_count_p;
1109 #else
1110 return g_atomic_int_get(ref_items.ref_count_p);
1111 #endif
1112 }
1113
1114 /**
1115 * The destructor does not throw unless the destructor of a managed
1116 * object throws - that should never happen.
1117 */
1118 ~SharedLockPtr() {unreference();}
1119 };
1120
1121 #if defined(CGU_USE_SMART_PTR_COMPARISON) || defined(DOXYGEN_PARSING)
1122
1123 // we can use built-in operator == when comparing pointers referencing
1124 // different objects of the same type
1125 /**
1126 * @ingroup handles
1127 *
1128 * This comparison operator does not throw. It compares the addresses
1129 * of the managed objects.
1130 *
1131 * Since 2.0.0-rc2
1132 */
1133 template <class T>
1134 bool operator==(const SharedPtr<T>& s1, const SharedPtr<T>& s2) noexcept {
1135 return (s1.get() == s2.get());
1136 }
1137
1138 /**
1139 * @ingroup handles
1140 *
1141 * This comparison operator does not throw. It compares the addresses
1142 * of the managed objects.
1143 *
1144 * Since 2.0.0-rc2
1145 */
1146 template <class T>
1147 bool operator!=(const SharedPtr<T>& s1, const SharedPtr<T>& s2) noexcept {
1148 return !(s1 == s2);
1149 }
1150
1151 // we must use std::less rather than the < built-in operator for
1152 // pointers to objects not within the same array or object: "For
1153 // templates greater, less, greater_equal, and less_equal, the
1154 // specializations for any pointer type yield a total order, even if
1155 // the built-in operators <, >, <=, >= do not." (para 20.3.3/8).
1156 /**
1157 * @ingroup handles
1158 *
1159 * This comparison operator does not throw unless std::less applied to
1160 * pointer types throws (which it would not do with any sane
1161 * implementation). It compares the addresses of the managed objects.
1162 *
1163 * Since 2.0.0-rc2
1164 */
1165 template <class T>
1166 bool operator<(const SharedPtr<T>& s1, const SharedPtr<T>& s2) {
1167 return std::less<T*>()(s1.get(), s2.get());
1168 }
1169
1170 /**
1171 * @ingroup handles
1172 *
1173 * This comparison operator does not throw. It compares the addresses
1174 * of the managed objects.
1175 *
1176 * Since 2.0.0-rc2
1177 */
1178 template <class T>
1179 bool operator==(const SharedLockPtr<T>& s1, const SharedLockPtr<T>& s2) noexcept {
1180 return (s1.get() == s2.get());
1181 }
1182
1183 /**
1184 * @ingroup handles
1185 *
1186 * This comparison operator does not throw. It compares the addresses
1187 * of the managed objects.
1188 *
1189 * Since 2.0.0-rc2
1190 */
1191 template <class T>
1192 bool operator!=(const SharedLockPtr<T>& s1, const SharedLockPtr<T>& s2) noexcept {
1193 return !(s1 == s2);
1194 }
1195
1196 /**
1197 * @ingroup handles
1198 *
1199 * This comparison operator does not throw unless std::less applied to
1200 * pointer types throws (which it would not do with any sane
1201 * implementation). It compares the addresses of the managed objects.
1202 *
1203 * Since 2.0.0-rc2
1204 */
1205 template <class T>
1206 bool operator<(const SharedLockPtr<T>& s1, const SharedLockPtr<T>& s2) {
1207 return std::less<T*>()(s1.get(), s2.get());
1208 }
1209
1210 #endif // CGU_USE_SMART_PTR_COMPARISON
1211
1212 } // namespace Cgu
1213
1214 // doxygen produces long filenames that tar can't handle:
1215 // we have generic documentation for std::hash specialisations
1216 // in doxygen.main.in
1217 #if defined(CGU_USE_SMART_PTR_COMPARISON) && !defined(DOXYGEN_PARSING)
1218 /* These structs allow SharedPtr and SharedLockPtr objects to be keys
1219 in unordered associative containers */
1220 namespace std {
1221 template <class T>
1222 struct hash<Cgu::SharedPtr<T>> {
1223 typedef std::size_t result_type;
1224 typedef Cgu::SharedPtr<T> argument_type;
1225 result_type operator()(const argument_type& s) const {
1226 // this is fine: std::hash structs do not normally contain data and
1227 // std::hash<T*> certainly won't, so we don't have overhead constructing
1228 // std::hash<T*> on the fly
1229 return std::hash<T*>()(s.get());
1230 }
1231 };
1232 template <class T>
1233 struct hash<Cgu::SharedLockPtr<T>> {
1234 typedef std::size_t result_type;
1235 typedef Cgu::SharedLockPtr<T> argument_type;
1236 result_type operator()(const argument_type& s) const {
1237 // this is fine: std::hash structs do not normally contain data and
1238 // std::hash<T*> certainly won't, so we don't have overhead constructing
1239 // std::hash<T*> on the fly
1240 return std::hash<T*>()(s.get());
1241 }
1242 };
1243 } // namespace std
1244 #endif // CGU_USE_SMART_PTR_COMPARISON
1245
1246 #endif