I am attempting to use std::unique_ptrs to manage Windows HANDLEs in an exception-safe manner.
First I tried:
struct HandleDeleter
{
void operator()( HANDLE handle )
{
if( handle )
{
FindVolumeClose( handle )
}
}
}
typedef std::unique_ptr< HANDLE, HandleDeleter > unique_vol_handle_t;
Later in my code when I try to use it:
unique_vol_handle_t volH( FindFirstVolumeW( buffer, MAX_GUID_PATH ) );
I get the following error from Visual Studio 2012RC:
1> error C2664: 'std::unique_ptr<_Ty,_Dx>::unique_ptr(std::nullptr_t) throw()' : cannot convert parameter 1 from 'HANDLE' to 'std::nullptr_t'
1> with
1> [
1> _Ty=HANDLE,
1> _Dx=VolumeHandleDeleter
1> ]
1> nullptr can only be converted to pointer or handle types
referencing the volH declaration line, immediately above.
After searching for some time, I found a blog article which basically says, add:
typedef HANDLE pointer;
to the top of the struct declaration, and all will be well.
I didn't believe it, but I tried it and it did resolve the error. I'm puzzled how defining a type (without even referencing it) could make such a difference.
Two questions:
1) Can you explain the original error? I don't understand why the compiler is referring to std::nullptr_t/nullptr
.
2) How is it that the typedef resolves this (or at least appears to)? Is there a less 'spooky action at a distance' solution to this?
Use unique_ptr when you want to have single ownership(Exclusive) of the resource. Only one unique_ptr can point to one resource. Since there can be one unique_ptr for single resource its not possible to copy one unique_ptr to another. A shared_ptr is a container for raw pointers.
Use unique_ptr when you want a single pointer to an object that will be reclaimed when that single pointer is destroyed. Use shared_ptr when you want multiple pointers to the same resource.
A unique_ptr does not share its pointer. It cannot be copied to another unique_ptr , passed by value to a function, or used in any C++ Standard Library algorithm that requires copies to be made. A unique_ptr can only be moved.
It can be assigned: class owner { std::unique_ptr<someObject> owned; public: owner() { owned=std::unique_ptr<someObject>(new someObject()); } };
The implementation of unique_ptr
checks for the presence of a ::pointer
type on the deleter. If the deleter has a ::pointer
type then this type is used as the pointer
typedef on the unique_ptr
. Otherwise a pointer to the first template argument is used.
According to cppreference.com, the unique_ptr::pointer
type is defined as
std::remove_reference<D>::type::pointer
if that type exists, otherwiseT*
From the MSDN manual on unique_ptr:
The stored pointer to an owned resource,
stored_ptr
has type pointer. It isDel::pointer
if defined, andType *
if not. The stored deleter objectstored_deleter
occupies no space in the object if the deleter is stateless. Note that Del can be a reference type.
This means that if you provide a deleter functor it have to provide a pointer
type that is used for the actual pointer type of the unique_ptr
. Otherwise it will be the a pointer to your provided type, in your case HANDLE*
which isn't correct.
I've been doing the following for various types of handles in Windows. Assuming we have declared somewhere:
std::unique_ptr<void, decltype (&FindVolumeClose)> fv (nullptr, FindVolumeClose);
This is populated with:
HANDLE temp = FindFirstVolume (...);
if (temp != INVALID_HANDLE_VALUE)
fv.reset (temp);
No need to declare a separate struct to wrap the deleters. Since HANDLE
is really a void *
the unique_ptr
takes void
as its type; for other kinds of handles, that use the DECLARE_HANDLE
macro, this can be avoided:
// Manages the life of a HHOOK
std::unique_ptr<HHOOK__, decltype (&UnhookWindowsHookEx)> hook (nullptr, UnhookWindowsHookEx);
And so on.
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With