To close a Windows handle with std::unique_ptr, we need to define a type with operator() and specify it as the second template parameter.
struct Deleter { // By defining the pointer type, we can delete a type other than T*. // In other words, we can declare unique_ptr<HANDLE, Deleter> instead of // unique_ptr<void, Deleter> which leaks the HANDLE abstraction. typedef HANDLE pointer; void operator()(HANDLE h) { if(h != INVALID_HANDLE_VALUE) { CloseHandle(h); } } }; void OpenAndWriteFile() { // Specify a deleter as a template argument. std::unique_ptr<HANDLE, Deleter> file(CreateFile(_T("test.txt"), GENERIC_WRITE, 0, NULL, CREATE_ALWAYS, FILE_ATTRIBUTE_NORMAL, NULL)); if(file.get() != INVALID_HANDLE_VALUE) { DWORD size; WriteFile(file.get(), "Hello World", 11, &size, NULL); } }
This is different from std::shared_ptr where we can just specify the functor in a constructor parameter.
std::shared_ptr<void> file(CreateFile(_T("test.txt"), GENERIC_WRITE, 0, NULL, CREATE_ALWAYS, FILE_ATTRIBUTE_NORMAL, NULL), CloseHandle); // Specify deleter functor as a constructor parameter.
Although it’s extra work to define a new deleter type, std::unique_ptr has following advantage.
First, the smart pointer object doesn’t have to keep the deleter object, thus the size of the pointer becomes smaller. In fact, std::unique_ptr occupies the just same size as T*.
Second, Application can overwrite the pointer type. It means we can write:
std::unique_ptr<HANDLE, Deleter> foo(...);
instead of this HANDLE abstraction leakage version.
std::unique_ptr<void, Deleter> foo(...);
Advertisements
Thanks, Moto.
I have two questions:
1) Can you clarify your comment in the code? It’s the thing I’m trying to understand, but there’s a grammatical error in the and I can’t deduce what you mean: “we can use delete a type other than T*”) ???
2) How does defining the pointer type help, when you never use it in your example? Is there some sort of side-effect from the declaration?
Thanks,
-Brad
Hi, thank you for the comment. I will fix the grammatical error in the code. I tried to answer your questions, but StackOverflow entry you made has better answers than I could ever write. Hope it clarifies the problem!
http://stackoverflow.com/a/12184983
Also, when I try something similar:
struct VolumeHandleDeleter
{
void operator()( HANDLE volH )
{
if( volH )
{
FindVolumeClose( volH );
}
}
};
typedef std::unique_ptr unique_vol_handle_t;
…
In main():
unique_vol_handle_t volH( FindFirstVolumeW( buffer, MAX_GUID_PATH ) ) );
VS2012 gives me:
1> error C2664: ‘std::unique_ptr::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 my volH declaration in main(). Any thoughts?
FindFirstVolumeW returns HANDLE.
Thanks!
Well I’ll be… Adding the typedef resolves the issue.
No idea why, so I’ve posted this as a StackOverflow question at http://stackoverflow.com/questions/12184779/using-stdunique-ptr-for-windows-handles