PStack  2.0
Stack trace printer for MSVC and GCC binaries
psystem::shared_handle< T, kInvalidHandle > Class Template Reference

Share management of a single "handle" between multiple owners. More...

#include <shared_handle.hpp>

Inheritance diagram for psystem::shared_handle< T, kInvalidHandle >:
Collaboration diagram for psystem::shared_handle< T, kInvalidHandle >:

Classes

struct  shared_handle_data
 The basic control block for a shared_handle. More...
 
struct  shared_handle_data_with_delete
 A control block for a shared_handle with a user-specified deleter. More...
 

Type Declarations

using handle_type = T
 The data-type of the handle being managed.
 
using safe_address_container = psystem::internal::managed_handle_proxy< handle_type, shared_handle< handle_type, kInvalidHandle > >
 A safe container for altering the handle by the address-of operator.
 

Construction / Destruction

The handle value that marks an "invalid" handle.

 shared_handle (handle_type hndl=kInvalidHandle) noexcept
 Construct an instance to manage a handle. More...
 
template<typename D >
 shared_handle (handle_type hndl, D &&deleter) noexcept
 Construct an instance to manage a handle with a custom "deleter.". More...
 
 shared_handle (shared_handle const &other) noexcept
 Share ownership of a handle with another shared_handle. More...
 
 shared_handle (shared_handle &&other)
 Transfer ownership of a handle from another shared_handle. More...
 
template<typename D >
 shared_handle (psystem::unique_handle< T, D, kInvalidHandle > &&other) noexcept
 Transfer ownership of a handle from an unique_handle. More...
 
 shared_handle (psystem::unique_handle< T, default_close_handle< T >, kInvalidHandle > &&other) noexcept
 Transfer ownership of a handle from a default unique_handle type. More...
 
 ~shared_handle () noexcept
 Release ownership of the handle by destroying this instance. More...
 

Operator Overloads

shared_handleoperator= (shared_handle const &o) noexcept
 Share ownership of a handle managed by a shared_handle instance. More...
 
shared_handleoperator= (shared_handle &&o) noexcept
 Transfer ownership of a handle into this insance. More...
 
template<typename D >
shared_handleoperator= (psystem::unique_handle< T, D, kInvalidHandle > &&o) noexcept
 Move an unique_handle instance into a shared_handle. More...
 
shared_handleoperator= (psystem::unique_handle< T, default_close_handle< T >, kInvalidHandle > &&o) noexcept
 Transfer ownership of a handle from a default unique_handle type. More...
 
safe_address_container operator& () noexcept
 Allow safe alteration of the handle by routines that return data by C-style output parameters. More...
 
 operator bool () const noexcept
 Determines the validity of this instance. More...
 

Public Interface

handle_type get () const noexcept
 Access the handle contained in this object. More...
 
void reset (handle_type hndl=kInvalidHandle) noexcept
 Assign a new handle to be managed by this instance. More...
 
template<typename D >
void reset (handle_type hndl, D &&deleter) noexcept
 Assign a new handle to be managed by this instance with a custom deleter. More...
 
void swap (shared_handle &o) noexcept
 Exchange ownership of two shared_handle instances. More...
 
bool unique () const noexcept
 Determines if this is the only instance managing the current handle. More...
 
size_t use_count () const noexcept
 The count of shared_handle instances managing this handle. More...
 

Private Utilities

void decrement () noexcept
 Decrement the managed handle's reference count. More...
 
void increment () noexcept
 Increment the managed handle's reference count.
 

Member Data

handle_type m_handle
 The handle to manage within this class.
 
shared_handle_datam_control_block
 The shared "control block" for all instances managing the handle. More...
 

Additional Inherited Members

- Static Public Member Functions inherited from psystem::stack_allocated
static void operator delete (void *)=delete
 Individual object deletion.
 
static void operator delete[] (void *)=delete
 Array of type object deletion.
 
static void * operator new (size_t)=delete
 Individual object allocation.
 
static void * operator new[] (size_t)=delete
 Array of type allocation.
 

Detailed Description

template<typename T = HANDLE, T kInvalidHandle = INVALID_HANDLE_VALUE>
class psystem::shared_handle< T, kInvalidHandle >

Share management of a single "handle" between multiple owners.

A handle in this context is any opaque item of data that is used (non-opaquely) by an API. Typically handles are received from some API invocation, and will need to be "closed" by a separate API to free any resources associated with that handle.

This class shares access and management of a handle between shared_handle instances by allocating a separate "control block." This way, the control block data can be observed by all shared_handle instances, and when a single instance is all that remains (see unique()), the handle can be closed on the deletion of the instance. The control block is also shared between "weak" references, so when no weak or strong reference containers exist, the control block must be deleted. Custom deletion for the handle exists within the control block as well — when the control block is destroyed, so will the deleter provided to it.

While it is possible to use std::shared_ptr with handles, it becomes somewhat ugly (the handle type must be void*, the handle must use nullptr as its "invalid value (Windows' INVALID_HANDLE_VALUE actually maps to 0xFFFFFFFF), and so on). Frankly, it's more fun to see what we can do by creating our own implementation, so here we are.

Invariant
If m_handle == invalid_handle_value, then m_control_block == nullptr.
Warning
This interface mimics std::shared_ptr, but it is not identical. Be aware of interface and behavioral differences by carefully reading documentation for individual methods.
Template Parameters
TThe type of the handle that is managed by this class.
kInvalidHandleFor a handle to be used by this class, it must have the concept of an "invalid" handle value. This concept is analogous to nullptr for pointer types. The value passed to this template paraameter represents the invalid value marker for the handle type managed by this class.
Author
Matt Bisson
Date
5 August, 2014
Since
PSystem 2.0
Version
PSystem 2.0
Todo:
Irritatingly, constexpr is not possible on Windows, which makes the invalid_handle_value member not possible. It would be great to change the instances of kInvalidHandle back to invalid_handle_value when MSVC brings itself up to three years ago.

Constructor & Destructor Documentation

template<typename T = HANDLE, T kInvalidHandle = INVALID_HANDLE_VALUE>
psystem::shared_handle< T, kInvalidHandle >::shared_handle ( handle_type  hndl = kInvalidHandle)
inlineexplicitnoexcept

Construct an instance to manage a handle.

Construction always succeeds, as exceptions are not allowed. If no parameter is specified, this constructs an instance that does not (initially) own an handle.

Parameters
[in]hndlThe handle (or invalid handle value) that this instance will manage.
template<typename T = HANDLE, T kInvalidHandle = INVALID_HANDLE_VALUE>
template<typename D >
psystem::shared_handle< T, kInvalidHandle >::shared_handle ( handle_type  hndl,
D &&  deleter 
)
inlinenoexcept

Construct an instance to manage a handle with a custom "deleter.".

Construction always succeeds, as exceptions are not allowed.

Warning
D must be copy-constructable or move-constructable, depending on how this constructor is invoked (see deleter parameter documentation), and that constructor may not throw.
Parameters
[in]hndlThe handle (or invalid handle value) that this instance will manage.
[in]deleterThe instance of an object used to clean up the handle that it managed by this instance. This is a "universal reference" (as Scott Meyers termed it), which means:
  • If deleter is passed by value, this parameter is a const&, and will be copied into this instance.
  • If deleter is passed by r-value, this parameter is an r-value reference, and will be moved into this instance.
  • If deleter is passed by reference, this parameter is that same reference type, and will be copied into this instance.
template<typename T = HANDLE, T kInvalidHandle = INVALID_HANDLE_VALUE>
psystem::shared_handle< T, kInvalidHandle >::shared_handle ( shared_handle< T, kInvalidHandle > const &  other)
inlinenoexcept

Share ownership of a handle with another shared_handle.

Warning
This differs from the std::shared_ptr in that it does not allow the transfer of related handle types. Often, handles are convertable types (void* to void* or long to long) that are merely obscured via a typedef. Transferring ownership of different handle types would also require changing the type of the deleter's type and the value of invalid_handle_value.
Parameters
[in]otherThe shared_handle with which we're sharing data.
template<typename T = HANDLE, T kInvalidHandle = INVALID_HANDLE_VALUE>
psystem::shared_handle< T, kInvalidHandle >::shared_handle ( shared_handle< T, kInvalidHandle > &&  other)
inline

Transfer ownership of a handle from another shared_handle.

Warning
This differs from the std::shared_ptr in that it does not allow the transfer of related handle types. Often, handles are convertable types (void* to void* or long to long) that are merely obscured via a typedef. Transferring ownership of different handle types would also require changing the type of the deleter's type and the value of invalid_handle_value.
Parameters
[in,out]otherThe shared_handle from which we're moving data.
template<typename T = HANDLE, T kInvalidHandle = INVALID_HANDLE_VALUE>
template<typename D >
psystem::shared_handle< T, kInvalidHandle >::shared_handle ( psystem::unique_handle< T, D, kInvalidHandle > &&  other)
inlineexplicitnoexcept

Transfer ownership of a handle from an unique_handle.

This method moves the handle managed by a unique_handle along with the deleter that is assigned to the instance.

Warning
This differs from the std::shared_ptr in that it does not allow the transfer of related handle types. Often, handles are convertable types (void* to void* or long to long) that are merely obscured via a typedef. Transferring ownership of different handle types would also require changing the type of the deleter's type and the value of invalid_handle_value.
D must be move-constructable, and it must not throw.
Template Parameters
DThe type of the "deleter" that must clean up the resources consumed by the handle that is managed by this class. The clean-up routine must match the function signature, "void(handle_type)", and it must not throw.
Parameters
[in,out]otherThe unique_handle from which we're moving data.
template<typename T = HANDLE, T kInvalidHandle = INVALID_HANDLE_VALUE>
psystem::shared_handle< T, kInvalidHandle >::shared_handle ( psystem::unique_handle< T, default_close_handle< T >, kInvalidHandle > &&  other)
inlineexplicitnoexcept

Transfer ownership of a handle from a default unique_handle type.

This method moves the handle managed by a unique_handle.

Warning
This differs from the std::shared_ptr in that it does not allow the transfer of related handle types. Often, handles are convertable types (void* to void* or long to long) that are merely obscured via a typedef. Transferring ownership of different handle types would also require changing the type of the deleter's type and the value of invalid_handle_value.
Parameters
[in,out]otherThe unique_handle from which we're moving data.
template<typename T = HANDLE, T kInvalidHandle = INVALID_HANDLE_VALUE>
psystem::shared_handle< T, kInvalidHandle >::~shared_handle ( )
inlinenoexcept

Release ownership of the handle by destroying this instance.

If this handle reports true from unique(), the handle will be closed, and the data associated with it will be cleaned (by the deleter, if specified).

If the handle is invalid_handle_value, this method does not call the deleter. The deleter contained within this instance will also be destroyed.

Warning
Neither the deleter, nor the D::operator() may throw.

Member Function Documentation

template<typename T = HANDLE, T kInvalidHandle = INVALID_HANDLE_VALUE>
void psystem::shared_handle< T, kInvalidHandle >::decrement ( )
inlineprivatenoexcept

Decrement the managed handle's reference count.

This method may have the side-effect of freeing the resources held by the handle (i.e., closing the handle) and possibly deleting the control block if the handle has been closed. If the handle is deleted, m_handle will be set to invalid_handle_value.

template<typename T = HANDLE, T kInvalidHandle = INVALID_HANDLE_VALUE>
handle_type psystem::shared_handle< T, kInvalidHandle >::get ( ) const
inlinenoexcept

Access the handle contained in this object.

Returns
The handle (it may or may not be valid). A valid handle returned by this method will remain so until the last shared_handle releases it.
template<typename T = HANDLE, T kInvalidHandle = INVALID_HANDLE_VALUE>
psystem::shared_handle< T, kInvalidHandle >::operator bool ( ) const
inlineexplicitnoexcept

Determines the validity of this instance.

This is comperable to a nullptr check for pointer types. The conversion is a "contextual" conversion, so it will work implicitly with boolean operators and if statements, but cannot implicitly convert to a boolean container.

Returns
true if this instance contains a valid handle, false, otherwise.
template<typename T = HANDLE, T kInvalidHandle = INVALID_HANDLE_VALUE>
safe_address_container psystem::shared_handle< T, kInvalidHandle >::operator& ( )
inlinenoexcept

Allow safe alteration of the handle by routines that return data by C-style output parameters.

Returns
A temporary object designed to track any changes to the handle when accessed by address. If the handle changes while the "safe" container tracks it, the prior handle will be released safely.
template<typename T = HANDLE, T kInvalidHandle = INVALID_HANDLE_VALUE>
shared_handle& psystem::shared_handle< T, kInvalidHandle >::operator= ( shared_handle< T, kInvalidHandle > const &  o)
inlinenoexcept

Share ownership of a handle managed by a shared_handle instance.

Warning
This differs from the std::shared_ptr in that it does not allow the transfer of related handle types. Often, handles are convertable types (void* to void* or long to long) that are merely obscured via a typedef. Transferring ownership of different handle types would also require changing the type of the deleter's type and the value of invalid_handle_value.
Note
This operation may not throw, but it provides a "strong" exception guarantee in case that requirement changes. That is, if there is an exception while moving the unique_handle, the current shared_handle instance is unmodified. Exception requirements can be relaxed when MSVC supports the noexcept keyword, and this property can be determined at using the noexcept operator.
Parameters
[in,out]oThe shared_handle with which we're sharing data.
Returns
A reference to this, after the assignment.
template<typename T = HANDLE, T kInvalidHandle = INVALID_HANDLE_VALUE>
shared_handle& psystem::shared_handle< T, kInvalidHandle >::operator= ( shared_handle< T, kInvalidHandle > &&  o)
inlinenoexcept

Transfer ownership of a handle into this insance.

Warning
This differs from the std::shared_ptr in that it does not allow the transfer of related handle types. Often, handles are convertable types (void* to void* or long to long) that are merely obscured via a typedef. Transferring ownership of different handle types would also require changing the type of the deleter's type and the value of invalid_handle_value.
Note
This operation may not throw, but it provides a "strong" exception guarantee in case that requirement changes. That is, if there is an exception while moving the unique_handle, the current shared_handle instance is unmodified. Exception requirements can be relaxed when MSVC supports the noexcept keyword, and this property can be determined at using the noexcept operator.
Parameters
[in,out]oThe shared_handle from which we're moving data.
Returns
A reference to this, after the assignment.
template<typename T = HANDLE, T kInvalidHandle = INVALID_HANDLE_VALUE>
template<typename D >
shared_handle& psystem::shared_handle< T, kInvalidHandle >::operator= ( psystem::unique_handle< T, D, kInvalidHandle > &&  o)
inlinenoexcept

Move an unique_handle instance into a shared_handle.

This method moves the handle managed by a unique_handle along with the deleter that is assigned to the instance.

Warning
This differs from the std::shared_ptr in that it does not allow the transfer of related handle types. Often, handles are convertable types (void* to void* or long to long) that are merely obscured via a typedef. Transferring ownership of different handle types would also require changing the type of the deleter's type and the value of invalid_handle_value.
Note
This operation may not throw, but it provides a "strong" exception guarantee in case that requirement changes. That is, if there is an exception while moving the unique_handle, the current shared_handle instance is unmodified. Exception requirements can be relaxed when MSVC supports the noexcept keyword, and this property can be determined at using the noexcept operator.
Template Parameters
DThe type of the "deleter" that must clean up the resources consumed by the handle that is managed by this class. The clean-up routine must match the function signature, "void(handle_type)", and it must not throw.
Parameters
[in,out]oThe unique_handle from which we're moving data.
Returns
A reference to this, after the assignment.
template<typename T = HANDLE, T kInvalidHandle = INVALID_HANDLE_VALUE>
shared_handle& psystem::shared_handle< T, kInvalidHandle >::operator= ( psystem::unique_handle< T, default_close_handle< T >, kInvalidHandle > &&  o)
inlinenoexcept

Transfer ownership of a handle from a default unique_handle type.

This method moves the handle managed by a unique_handle.

Warning
This differs from the std::shared_ptr in that it does not allow the transfer of related handle types. Often, handles are convertable types (void* to void* or long to long) that are merely obscured via a typedef. Transferring ownership of different handle types would also require changing the type of the deleter's type and the value of invalid_handle_value.
Note
This operation may not throw, but it provides a "strong" exception guarantee in case that requirement changes. That is, if there is an exception while moving the unique_handle, the current shared_handle instance is unmodified. Exception requirements can be relaxed when MSVC supports the noexcept keyword, and this property can be determined at using the noexcept operator.
Parameters
[in,out]oThe unique_handle from which we're moving data.
Returns
A reference to this, after the assignment.
template<typename T = HANDLE, T kInvalidHandle = INVALID_HANDLE_VALUE>
void psystem::shared_handle< T, kInvalidHandle >::reset ( handle_type  hndl = kInvalidHandle)
inlinenoexcept

Assign a new handle to be managed by this instance.

If there was a valid handle managed previously by this shared_handle instance, its resources may be cleaned up prior to assigning the new handle. If the new handle equals (==) the old handle, this method does nothing.

If no parameters are specified, this resets the shared_handle to the "invalid" state.

Parameters
[in]hndlThe new handle to manage. It need not be "valid."
template<typename T = HANDLE, T kInvalidHandle = INVALID_HANDLE_VALUE>
template<typename D >
void psystem::shared_handle< T, kInvalidHandle >::reset ( handle_type  hndl,
D &&  deleter 
)
inlinenoexcept

Assign a new handle to be managed by this instance with a custom deleter.

If there was a valid handle managed previously by this shared_handle instance, its resources may be cleaned up prior to assigning the new handle. If the new handle equals (==) the old handle, this method does nothing.

If no parameters are specified, this resets the shared_handle to the "invalid" state.

Template Parameters
DThe type of the "deleter" that must clean up the resources consumed by the handle that is managed by this class. The clean-up routine must match the function signature, "void(handle_type)", and it must not throw.
Parameters
[in]hndlThe new handle to manage. It need not be "valid."
[in]deleterThe instance of an object used to clean up the handle that it managed by this instance. This is a "universal reference" (as Scott Meyers termed it), which means:
  • If deleter is passed by value, this parameter is a const&, and will be copied into this instance.
  • If deleter is passed by r-value, this parameter is an r-value reference, and will be moved into this instance.
  • If deleter is passed by reference, this parameter is that same reference type, and will be copied into this instance.
template<typename T = HANDLE, T kInvalidHandle = INVALID_HANDLE_VALUE>
void psystem::shared_handle< T, kInvalidHandle >::swap ( shared_handle< T, kInvalidHandle > &  o)
inlinenoexcept

Exchange ownership of two shared_handle instances.

Parameters
[in,out]oThe shared_handle instance that will swap its handle and control block with this instance.
template<typename T = HANDLE, T kInvalidHandle = INVALID_HANDLE_VALUE>
bool psystem::shared_handle< T, kInvalidHandle >::unique ( ) const
inlinenoexcept

Determines if this is the only instance managing the current handle.

Returns
This method returns true if the reference count is 1. If the handle being managed is invalid, the return is false.
template<typename T = HANDLE, T kInvalidHandle = INVALID_HANDLE_VALUE>
size_t psystem::shared_handle< T, kInvalidHandle >::use_count ( ) const
inlinenoexcept

The count of shared_handle instances managing this handle.

Returns
A value from [0,SIZE_MAX]. If the handle being managed is invalid, the method returns 0.

Member Data Documentation

template<typename T = HANDLE, T kInvalidHandle = INVALID_HANDLE_VALUE>
shared_handle_data* psystem::shared_handle< T, kInvalidHandle >::m_control_block
private

The shared "control block" for all instances managing the handle.

The control block is passed from shared_handle to shared_handle when managing the same handle_type instance. It is called the control block because it contains all the reference counts (and the deleter) to determine when the data may be cleaned up.

When m_control_block.m_usage_count is 0, the m_handle may be closed. When m_control_block.m_weak_count is 0, m_control_block itself shall be freed.

Note
When m_handle is invalid_handle_value, m_control_block is nullptr.

The documentation for this class was generated from the following file: