Skip to content

RAII resource wrapper details

Charles Milette edited this page May 14, 2019 · 3 revisions

This page documents the details of the WIL resource management classes (resource.h), for those who need to integrate with or debug those classes.

Glossary

  • ownership
    The obligation to close or otherwise dispose of another object.
  • empty
    The state of a resource type when it does not own anything.

Resource policy

The wil::details::resource_policy traits class decribes how an object is managed by the wil library's unique_* classes.

Use this value if you are defining a new resource type and you need finer control than unique_any_t provides.

template <typename pointer,
          typename close_fn_t,
          close_fn_t close_fn,
          typename pointer_access = wil::details::pointer_access_all,
          typename pointer_storage = pointer,
          pointer invalid = pointer(),
          typename pointer_invalid = wistd::nullptr_t>
struct resource_policy;

Template parameters

  • pointer
    The underlying data type that is managed by the unique_* wrapper. For types wrapped by unique_ptr, it is typically a pointer type.
  • close_fn_t
    The type of the function that is used to close the underlying resource.
    • The close_fn_t and close_fn are usually passed as the pair decltype(something), something.
  • close_fn
    A unary operation (typically a function) which closes the underlying resource.
    • It is called with one parameter, a pointer_storage containing the resource to be closed.
    • It will not be called with the invalid value.
  • pointer_access (default is pointer_access_all)
    A pointer access value (see below) which controls visibility of the underlying pointer to clients.
  • pointer_storage (default is pointer)
    The type used to store the underlying data type. This is usually the same as pointer, but will be different if the underlying storage is itself a wrapper class, such as a smart pointer. The pointer_storage must have an implicit conversion to pointer and vice versa.
  • invalid (default is null/zero value of pointer)
    The value which represents that there is no resource. Defaults to the null/zero value of the pointer type.
  • pointer_invalid (default is wistd::nullptr_t)
    The type wistd::nullptr_t if you are allowed to reset or assign nullptr to the unique_* wrapper. If the underlying data type is not a pointer type, then pass pointer instead.

Member types

  • pointer_storage
    The pointer_storage type specified in the template.
  • pointer
    The pointer type specified in the template.
  • pointer_access
    The pointer_access value specified in the template.
  • pointer_invalid
    The pointer_invalid type specified in the template.

Methods

  • static pointer_storage invalid_value()
    Produces the invalid value.
  • static bool is_valid(pointer_storage value)
    Reports whether the value is the invalid value.
  • static void close(pointer_storage value)
    Closes the resource represented by value, which will never be invalid.
  • static void close_reset(pointer_storage value)
    Closes the resource represented by value, which will never be invalid, while preserving the GetLastError value.

Pointer access policy

The wil::details::pointer_access_* values control how much access the client of a unique_* wrapper has to the wrapped pointer value.

Use this value if you are defining a new resource type and wish to restrict access to the raw pointer.

Value Can move Can get() Can release() Can addressof() and &
wil::details::pointer_access_all Yes Yes Yes Yes
wil::details::pointer_access_noaddress Yes Yes Yes No
wil::details::pointer_access_none Yes No No No

Unique storage

The wil::details::unique_storage template class is a low-level smart pointer class which provides the underlying storage and memory management operations for the unique_* wrappers. The unique_storage is an RAII class which manages the lifetime of another object (the owned object). If the unique_storage is not managing any object, it is said to be empty.

A unique_storage is the size of a pointer_storage, which is typically a pointer.

You usually do not need to use this class directly.

It is separate from unique_any_t to allow a type-specific specialization class to insert itself into the inheritance chain between unique_any_t and unique_storage. Classes like unique_event take advantage of this so that they can be a unique_any, but with additional methods like SetEvent.

template <typename policy>
struct unique_storage;

Template parameters

  • policy
    The resource_policy that describes the behavior of the resource.

Member types

  • policy (protected)
    The resource_policy type specified in the template.
  • pointer_storage (protected)
    The pointer_storage type of the policy.
  • pointer (protected)
    The pointer type of the policy.
  • base_storage (protected)
    The unique_storage class itself.

Constructors

  • unique_storage() (protected)
    Initializes an empty storage.
  • unique_storage(pointer_storage ptr) (protected)
    Takes ownership of the provided pointer.

Methods

  • pointer get() const
    Returns a pointer to the object being managed, or a policy-defined invalid value (usually a null pointer) if the unique_storage is empty.
  • void reset(pointer_storage ptr)
    Closes the pointer currently being managed and takes ownership of the new pointer.
  • void reset()
    Closes the pointer currently being managed and leaves the object empty.
  • void reset(nullptr)
    Closes the pointer currently being managed and leaves the object empty. Allowed only if the underlying type is a pointer. (The policy::pointer_invalid must be wistd::nullptr_t.)
  • pointer_storage release()
    Makes the unique_storage empty and returns the object that was previously being managed. It is the caller's responsibility to close the returned object. (The pointer access policy must be all or noaddress.)
  • pointer_storage* addressof()
    Returns a pointer to the internal pointer storage. (The pointer access policy must be all.)
  • void replace(unique_storage&& other) (protected)
    Closes the pointer currently being managed and takes ownership of the pointer managed by the other object.
  • bool is_valid() const (protected)
    Returns true if an object is being managed, or or false if the unique_storage is empty.

Note that the release method behaves differently from the same-named method of COM and MFC wrapper classes. The behavior of the unique_storage aligns with the C++ standard.

unique_storage COM/MFC wrappers Description
reset Attach Take ownership of a raw pointer.
release Detach Return a raw pointer and transfer the ownership to the caller.

Advanced use of unique storage

As noted above, you can derive from unique_storage in order to inject methods into the underlying type. For example, consider a platform type HWIDGET. You could start with a unique_any declaration:

using unique_widget = unique_any<HWIDGET, decltype(::CloseWidget), ::CloseWidget>;

Operations on widgets would go like this:

unique_widget widget{ CreateWidget() };
WiggleWidget(widget.get());

If you desire to make wiggling a widget more convenient, you could inject the wiggle method into the unique_widget as follows:

class widget_t :
    public wil::details::unique_storage<
        wil::details::resource_policy<HWIDGET, decltype(::CloseWidget), ::CloseWidget>>
{
public:
  // Give the storage type a simpler name.
  typedef wil::details::unique_storage<
        wil::details::resource_policy<HWIDGET, decltype(::CloseWidget), ::CloseWidget>> storage_t;

  // forward all base class constructors
  template<typename... args_t>
  explicit widget_t(args_t&& ...args) noexcept : storage_t(wistd::forward<args_t>(args)...) {}

  // Here is where we can inject additional methods.
  void wiggle() const noexcept
  {
    WiggleWidget(storage_t::get());
  }
}

using unique_widget = unique_any_t<widget_t>;

You can now invoke the wiggle method directly on a unique_widget:

unique_widget widget{ CreateWidget() };
widget.wiggle();

Use this pattern sparingly. It works best for types (such as kernel events) where clients are not really interested in the handle itself, but rather on the operations availble to the handle.

Unique any

The wil::unique_any_t template class is a smart pointer class. It manages the lifetime of another object (the owned object). If the unique_any_t is not managing any object, it is said to be empty.

A unique_any_t is the size of a pointer_storage, which is typically a pointer.

The unique_any_t type is patterned after the C++ unique_ptr type. It is movable but not copyable.

template <typename storage_t>
class unique_any_t : public storage_t;

Template parameters

  • storage_t
    A type that ultimately derives from unique_storage. In most cases, this is a specialization of unique_storage itself. (See Advanced use of unique storage for an example of using some other type.)

Member types

  • policy
    The resource_policy of the unique_storage.
  • pointer_storage
    The pointer_storage type of the policy.
  • pointer
    The pointer type of the policy.

Constructors

  • unique_any_t(args...)
    Constructor parameters are forwarded to the storage_t. In the common case where the storage_t is a unique_storage, it takes ownership of the passed-in pointer.
  • unique_any_t()
    Creates an empty unique_any_t.
  • unique_any_t(nullptr)
    Creates an empty unique_any_t. (The policy::pointer_invalid must be wistd::nullptr_t.)

Operators

  • operator=(unique_any_t&& other) noexcept
    Closes the object currently being managed and takes ownership of the object being managed by other.
  • operator=(nullptr) noexcept
    Closes the object currently being managed and leaves the unique_any_t empty. Allowed only if the underlying type is a pointer. (The policy::pointer_invalid must be wistd::nullptr_t.)
  • explicit operator bool() const noexcept
    Returns true if an object is being managed, or or false if the unique_any_t is empty.
  • pointer_storage* operator&() noexcept
    Closes the object that is currently being managed and returns a pointer to the internal pointer storage. (The pointer access policy must be all.) To obtain a pointer to the internal pointer storage without closing the current object, use the addressof method.
  • ==, !=, <, <=, >, >= comparison operators
    These perform comparison on the underlying pointer. If the policy::pointer_invalid is wistd::nullptr_t, then equality and inequality comparisons with nullptr are also supported.

Methods

  • pointer get() const
    Returns a pointer to the object being managed, or a policy-defined invalid value (usually a null pointer) if the unique_any_t is empty. (The pointer access policy must be all or noaddress.)
  • void reset(pointer_storage ptr) (inherited)
    Closes the object currently being managed and takes ownership of the new pointer.
  • void reset() (inherited)
    Closes the object currently being managed and leaves the unique_any_t empty.
  • void reset(nullptr) (inherited)
    Closes the object currently being managed and leaves the unique_any_t empty. Allowed only if the underlying type is a pointer. (The policy::pointer_invalid must be wistd::nullptr_t.)
  • pointer_storage release() (inherited)
    Makes the unique_any_t empty and returns the object that was previously being managed. It is the caller's responsibility to close the returned object. (The pointer access policy must be all or noaddress.)
  • pointer_storage* put()
    Closes the object that is currently being managed and returns a pointer to the internal pointer storage. (The pointer access policy must be all.) To obtain a pointer to the internal pointer storage without closing the current object, use the addressof method.
  • pointer_storage* addressof() (inherited)
    Returns a pointer to the internal pointer storage. (The pointer access policy must be all.)
  • swap(unique_any_t& other) noexcept
    Exchanges the owned objects. Also available as a free function swap(left, right) for ADL purposes.

Unique any alias

The unique_any alias makes it more convenient to create new unique_* types.

template <typename pointer,
          typename close_fn_t,
          close_fn_t close_fn,
          typename pointer_access = wil::details::pointer_access_all,
          typename pointer_storage = pointer,
          pointer invalid = pointer(),
          typename pointer_invalid = wistd::nullptr_t>
using unique_any = unique_any_t<details::unique_storage<details::resource_policy<
          pointer, close_fn_t, close_fn, pointer_access, pointer_storage, invalid, pointer_invalid>>>;

The template parameters are the same as those for resource_policy.