PStack
2.0
Stack trace printer for MSVC and GCC binaries
|
Share management of a single "handle" between multiple owners. More...
#include <shared_handle.hpp>
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 | |
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_handle & | operator= (shared_handle const &o) noexcept |
Share ownership of a handle managed by a shared_handle instance. More... | |
shared_handle & | operator= (shared_handle &&o) noexcept |
Transfer ownership of a handle into this insance. More... | |
template<typename D > | |
shared_handle & | operator= (psystem::unique_handle< T, D, kInvalidHandle > &&o) noexcept |
Move an unique_handle instance into a shared_handle. More... | |
shared_handle & | operator= (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_data * | m_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. | |
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.
m_handle
== invalid_handle_value
, then m_control_block
== nullptr
.std::shared_ptr
, but it is not identical. Be aware of interface and behavioral differences by carefully reading documentation for individual methods.T | The type of the handle that is managed by this class. |
kInvalidHandle | For 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. |
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.
|
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.
[in] | hndl | The handle (or invalid handle value) that this instance will manage. |
|
inlinenoexcept |
Construct an instance to manage a handle with a custom "deleter.".
Construction always succeeds, as exceptions are not allowed.
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.[in] | hndl | The handle (or invalid handle value) that this instance will manage. |
[in] | deleter | The 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:
|
|
inlinenoexcept |
Share ownership of a handle with another shared_handle.
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
.[in] | other | The shared_handle with which we're sharing data. |
|
inline |
Transfer ownership of a handle from another shared_handle.
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
.[in,out] | other | The shared_handle from which we're moving data. |
|
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.
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.D | The 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. |
[in,out] | other | The unique_handle from which we're moving data. |
|
inlineexplicitnoexcept |
Transfer ownership of a handle from a default unique_handle type.
This method moves the handle managed by a unique_handle.
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
.[in,out] | other | The unique_handle from which we're moving data. |
|
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.
D::operator()
may throw.
|
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
.
|
inlinenoexcept |
Access the handle contained in this object.
|
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.
true
if this instance contains a valid handle, false
, otherwise.
|
inlinenoexcept |
Allow safe alteration of the handle by routines that return data by C-style output parameters.
|
inlinenoexcept |
Share ownership of a handle managed by a shared_handle instance.
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
.noexcept
keyword, and this property can be determined at using the noexcept
operator.[in,out] | o | The shared_handle with which we're sharing data. |
this
, after the assignment.
|
inlinenoexcept |
Transfer ownership of a handle into this insance.
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
.noexcept
keyword, and this property can be determined at using the noexcept
operator.[in,out] | o | The shared_handle from which we're moving data. |
this
, after the assignment.
|
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.
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
.noexcept
keyword, and this property can be determined at using the noexcept
operator.D | The 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. |
[in,out] | o | The unique_handle from which we're moving data. |
this
, after the assignment.
|
inlinenoexcept |
Transfer ownership of a handle from a default unique_handle type.
This method moves the handle managed by a unique_handle.
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
.noexcept
keyword, and this property can be determined at using the noexcept
operator.[in,out] | o | The unique_handle from which we're moving data. |
this
, after the assignment.
|
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.
[in] | hndl | The new handle to manage. It need not be "valid." |
|
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.
D | The 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. |
[in] | hndl | The new handle to manage. It need not be "valid." |
[in] | deleter | The 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:
|
|
inlinenoexcept |
Exchange ownership of two shared_handle instances.
[in,out] | o | The shared_handle instance that will swap its handle and control block with this instance. |
|
inlinenoexcept |
Determines if this is the only instance managing the current handle.
true
if the reference count is 1. If the handle being managed is invalid, the return is false
.
|
inlinenoexcept |
The count of shared_handle instances managing this handle.
|
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.
m_handle
is invalid_handle_value
, m_control_block
is nullptr
.