c++-gtk-utils
Public Member Functions | List of all members
Cgu::SharedHandle< T, Dealloc > Class Template Reference

This is a generic class for managing the lifetime of objects allocated on freestore. More...

#include <c++-gtk-utils/shared_handle.h>

Public Member Functions

 SharedHandle (T ptr=0)
 
 SharedHandle (T ptr, Cgu::SharedHandleAllocFail::Leave tag)
 
void reset (T ptr=0)
 
void reset (T ptr, Cgu::SharedHandleAllocFail::Leave tag)
 
 SharedHandle (const SharedHandle &sh_hand) noexcept
 
 SharedHandle (SharedHandle &&sh_hand) noexcept
 
SharedHandleoperator= (SharedHandle sh_hand)
 
get () const noexcept
 
 operator T () const noexcept
 
unsigned int get_refcount () const noexcept
 
 ~SharedHandle ()
 

Detailed Description

template<class T, class Dealloc = StandardArrayDelete<T>>
class Cgu::SharedHandle< T, Dealloc >

This is a generic class for managing the lifetime of objects allocated on freestore.

See also
SharedLockHandle
ScopedHandle
SharedHandleError
GcharSharedHandle
GerrorSharedHandle
StandardArrayDelete CFree GFree GerrorFree GSliceFree GSliceFreeSize GSliceDestroy

The SharedHandle class is similar to the SharedPtr class (it keeps a reference count and deletes the handled object when the count reaches 0), but it does not have pointer semantics. Accordingly, it can be used to manage the memory of arrays and other objects allocated on the heap.

Because it is useful with arrays, by default it deallocates memory using C++ delete[]. However, if a SharedHandle object is passed a function object type as a second template argument when instantiated, it will use that function object to delete memory. This enables it to handle the memory of any object, such as objects to be deleted using std::free() or Glib's g_free(), g_list_free() or g_slice_free(). Instances (such as GcharScopedHandle, GcharSharedHandle, GerrorSharedHandle and GerrorScopedHandle) typdef'ed for particular deleters can conveniently manage objects of any kind.

To reflect the fact that it is just a handle for a pointer, it has different instantiation semantics from a SharedPtr object. A SharedPtr object is instantiated using this syntax:

SharedPtr<ObjType> sh_ptr(new ObjType);

A SharedHandle is instantiated using this syntax (note that the instantiated handle is for type T* and not T):

SharedHandle<ObjType*> sh_handle(new ObjType[n]);

Apart from the operatorT() type conversion operator (which returns the underlying pointer), the only other method to obtain the underlying pointer is the get() method. If the object referenced is an array allocated on the heap, to use indexing you could either do this:

using namespace Cgu;
SharedHandle<char*> handle(new char[10]);
handle.get()[0] = 'a';
std::cout << handle.get()[0] << std::endl;

or this:

using namespace Cgu;
SharedHandle<char*> handle(new char[10]);
handle[0] = 'a';
std::cout << handle[0] << std::endl;

There is also a SharedLockHandle class, which has a thread-safe reference count, and a ScopedHandle class, which deletes its object as soon as it goes out of scope. A ScopedHandle class can be viewed as a SharedHandle which cannot be assigned to or used as the argument to a copy constructor and therefore which cannot have a reference count of more than 1. It is used where, if you wanted pointer semantics, you might use a const std::auto_ptr<>.

SharedHandle objects can be instantiated for pointers to constant objects (such as SharedHandle<const char*>), provided the deleter functor will take such pointers.

This library provides StandardArrayDelete, CFree, GFree, GerrorFree, GSliceFree, GSliceFreeSize and GSliceDestroy deleter functors, which can be used as the second template parameter of the SharedHandle class. As mentioned above, StandardArrayDelete is the default, and some typedef'ed instances of SharedHandle for gchar (with the GFree deleter) and for GError (with the GerrorFree deleter) are provided.

Comparison with std::shared_ptr

Although the semantics of std::shared_ptr in C++11/14 are not particularly suited to managing either arrays or C objects with accessor functions (such as in glib), most of the things that can be done by this class can be done by using std::shared_ptr with a specialised deleter. However, this class is retained in the c++-gtk-utils library not only to retain compatibility with series 1.2 of the library, but also to cater for some cases not met (or not so easily met) by std::shared_ptr:

  1. The Cgu::SharedHandle class takes its deleter as a template parameter, which means that typedefs can be used to enable handles for particular deleters to be easily created (and as mentioned, this library provides a number of pre-formed deleter functors and typedefs for them). With std::shared_ptr, custom deleters must be passed to the shared_ptr constructor on every occasion a shared_ptr is constructed to manage a new object (and they cannot be templated as a typedef).
  2. Glib memory slices provide an efficient small object allocator (they are likely to be significantly more efficient than global operator new()/new[](), which generally hand off to malloc(), and whilst malloc() is good for large block allocations it is generally poor as a small object allocator). Internal Cgu::SharedHandle allocation using glib memory slices can be achieved by compiling the library with the --with-glib-memory-slices-no-compat configuration option.
  3. If glib memory slices are not used (which do not throw), constructing a shared pointer for a new managed object (or calling reset() for a new managed object) might throw if internal allocation fails. Although by default the Cgu::SharedHandle implementation will delete the new managed object in such a case, it also provides an alternative constructor and reset() method which instead enable the new object to be accessed via the thrown exception object so that user code can decide what to do; std::shared_ptr deletes the new object in every case.
  4. A user can explicitly state whether the shared handle object is to have atomic increment and decrement-and-test with respect to the reference count so that the reference count is thread safe ('no' in the case of Cgu::SharedHandle, and 'yes' in the case of Cgu::SharedLockHandle). Using atomic functions is unnecessary if the managed object concerned is only addressed in one thread (and might cause unwanted cache flushing in certain circumstances). std::shared_ptr will generally always use atomic functions with respect to its reference count in a multi-threaded program.

In favour of std::shared_ptr, it has an associated std::weak_ptr class, which Cgu::SharedHandle does not (there is a Cgu::GobjWeakHandle class, but that is cognate with Cgu::GobjHandle and is only usable with GObjects).

If the library is compiled with the --with-glib-memory-slices-no-compat configuration option, as mentioned Cgu::SharedHandle constructs its reference counting internals using glib memory slices. Although it is safe in a multi-threaded program if glib < 2.32 is installed to construct a static SharedHandle object in global namespace (that is, prior to g_thread_init() being called) by means of the default constructor and/or a pointer argument of NULL, it is not safe if constructed with a non-NULL pointer value. If glib >= 2.32 is installed, global objects with memory slices are safe in all circumstances. (Having said that, it would be highly unusual to have global SharedHandle objects.)

Constructor & Destructor Documentation

◆ SharedHandle() [1/4]

template<class T , class Dealloc = StandardArrayDelete<T>>
Cgu::SharedHandle< T, Dealloc >::SharedHandle ( ptr = 0)
inlineexplicit

Constructor taking an unmanaged object.

Parameters
ptrThe object which the SharedHandle is to manage (if any).
Exceptions
std::bad_allocThis constructor will not throw if the 'ptr' argument has a NULL value (the default), otherwise it might throw std::bad_alloc if memory is exhausted and the system throws in that case. If such an exception is thrown, this constructor is exception safe (it does not leak resources), but as well as cleaning itself up this constructor will also delete the managed object passed to it to avoid a memory leak. If such automatic deletion is not wanted in that case, use the version of this constructor taking a Cgu::SharedHandleAllocFail::Leave tag argument.
Note
std::bad_alloc will not be thrown if the library has been installed using the --with-glib-memory-slices-no-compat configuration option: instead glib will terminate the program if it is unable to obtain memory from the operating system.

◆ SharedHandle() [2/4]

template<class T , class Dealloc = StandardArrayDelete<T>>
Cgu::SharedHandle< T, Dealloc >::SharedHandle ( ptr,
Cgu::SharedHandleAllocFail::Leave  tag 
)
inline

Constructor taking an unmanaged object.

Parameters
ptrThe object which the SharedHandle is to manage
tagPassing the tag emumerator Cgu::SharedHandleAllocFail::leave causes this constructor not to delete the new managed object passed as the 'ptr' argument in the event of internal allocation in this method failing because of memory exhaustion (in that event, Cgu::SharedHandleError will be thrown).
Exceptions
Cgu::SharedHandleErrorThis constructor might throw Cgu::SharedHandleError if memory is exhausted and the system would otherwise throw std::bad_alloc in that case. This constructor is exception safe (it does not leak resources), and if such an exception is thrown it will clean itself up, but it will not attempt to delete the new managed object passed to it. Access to the object passed to the 'ptr' argument can be obtained via the thrown Cgu::SharedHandleError object.
Note
1. On systems with over-commit/lazy-commit combined with virtual memory (swap), it is rarely useful to check for memory exhaustion, so in those cases this version of the constructor will not be useful.
2. If the library has been installed using the --with-glib-memory-slices-no-compat configuration option this version of the constructor will also not be useful: instead glib will terminate the program if it is unable to obtain memory from the operating system.

◆ SharedHandle() [3/4]

template<class T , class Dealloc = StandardArrayDelete<T>>
Cgu::SharedHandle< T, Dealloc >::SharedHandle ( const SharedHandle< T, Dealloc > &  sh_hand)
inlinenoexcept

The copy constructor does not throw.

Parameters
sh_handThe handle to be copied.

◆ SharedHandle() [4/4]

template<class T , class Dealloc = StandardArrayDelete<T>>
Cgu::SharedHandle< T, Dealloc >::SharedHandle ( SharedHandle< T, Dealloc > &&  sh_hand)
inlinenoexcept

The move constructor does not throw. It has move semantics.

Parameters
sh_handThe handle to be moved.

◆ ~SharedHandle()

template<class T , class Dealloc = StandardArrayDelete<T>>
Cgu::SharedHandle< T, Dealloc >::~SharedHandle ( )
inline

The destructor does not throw unless the destructor of a handled object throws - that should never happen.

Member Function Documentation

◆ get()

template<class T , class Dealloc = StandardArrayDelete<T>>
T Cgu::SharedHandle< T, Dealloc >::get ( ) const
inlinenoexcept

This method does not throw.

Returns
A pointer to the handled object (or NULL if none is handled).

◆ get_refcount()

template<class T , class Dealloc = StandardArrayDelete<T>>
unsigned int Cgu::SharedHandle< T, Dealloc >::get_refcount ( ) const
inlinenoexcept

This method does not throw.

Returns
The number of SharedHandle objects referencing the managed object (or 0 if none is managed by this SharedHandle).

◆ operator T()

template<class T , class Dealloc = StandardArrayDelete<T>>
Cgu::SharedHandle< T, Dealloc >::operator T ( ) const
inlinenoexcept

This method does not throw.

Returns
A pointer to the handled object (or NULL if none is handled).

◆ operator=()

template<class T , class Dealloc = StandardArrayDelete<T>>
SharedHandle& Cgu::SharedHandle< T, Dealloc >::operator= ( SharedHandle< T, Dealloc >  sh_hand)
inline

This method (and so copy or move assignment) does not throw unless the destructor of a managed object throws.

Parameters
sh_handthe assignor.
Returns
The SharedHandle object after assignment.

◆ reset() [1/2]

template<class T , class Dealloc = StandardArrayDelete<T>>
void Cgu::SharedHandle< T, Dealloc >::reset ( ptr,
Cgu::SharedHandleAllocFail::Leave  tag 
)
inline

Causes the SharedHandle to cease to manage its managed object (if any), deleting it if this is the last SharedHandle object managing it. The SharedHandle object will manage the new object passed (which must not be managed by any other SharedHandle object). This method is exception safe, but see the comments below on Cgu::SharedHandleError.

Parameters
ptrA new unmanaged object to manage (if no new object is to be managed, use the version of reset() taking a default value of NULL).
tagPassing the tag emumerator Cgu::SharedHandleAllocFail::leave causes this method not to delete the new managed object passed as the 'ptr' argument in the event of internal allocation in this method failing because of memory exhaustion (in that event, Cgu::SharedHandleError will be thrown).
Exceptions
Cgu::SharedHandleErrorThis method might throw Cgu::SharedHandleError if memory is exhausted and the system would otherwise throw std::bad_alloc in that case. Note that if such an exception is thrown then this method will do nothing (it is strongly exception safe and will continue to manage the object it was managing prior to the call), and it will not attempt to delete the new managed object passed to it. Access to the object passed to the 'ptr' argument can be obtained via the thrown Cgu::SharedHandleError object.
Note
1. On systems with over-commit/lazy-commit combined with virtual memory (swap), it is rarely useful to check for memory exhaustion, so in those cases this version of the reset() method will not be useful.
2. If the library has been installed using the --with-glib-memory-slices-no-compat configuration option this version of the reset() method will also not be useful: instead glib will terminate the program if it is unable to obtain memory from the operating system.

◆ reset() [2/2]

template<class T , class Dealloc = StandardArrayDelete<T>>
void Cgu::SharedHandle< T, Dealloc >::reset ( ptr = 0)
inline

Causes the SharedHandle to cease to manage its managed object (if any), deleting it if this is the last SharedHandle object managing it. If the argument passed is not NULL, the SharedHandle object will manage the new object passed (which must not be managed by any other SharedHandle object). This method is exception safe, but see the comments below on std::bad_alloc.

Parameters
ptrNULL (the default), or a new unmanaged object to manage.
Exceptions
std::bad_allocThis method will not throw if the 'ptr' argument has a NULL value (the default) and the destructor of a managed object does not throw, otherwise it might throw std::bad_alloc if memory is exhausted and the system throws in that case. Note that if such an exception is thrown then this method will do nothing (it is strongly exception safe and will continue to manage the object it was managing prior to the call), except that it will delete the new managed object passed to it to avoid a memory leak. If such automatic deletion in the event of such an exception is not wanted, use the reset() method taking a Cgu::SharedHandleAllocFail::Leave tag type as its second argument.
Note
std::bad_alloc will not be thrown if the library has been installed using the --with-glib-memory-slices-no-compat configuration option: instead glib will terminate the program if it is unable to obtain memory from the operating system.

The documentation for this class was generated from the following file:
Cgu
Definition: application.h:44
Cgu::SharedHandle
This is a generic class for managing the lifetime of objects allocated on freestore.
Definition: shared_handle.h:544