std::unique_ptr for Windows handles

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(...);
About these ads

About Moto

Engineer who likes coding
This entry was posted in C++ and tagged , . Bookmark the permalink.

4 Responses to std::unique_ptr for Windows handles

  1. Brad says:

    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

  2. Brad says:

    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!

  3. Brad says:

    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

Leave a Reply

Fill in your details below or click an icon to log in:

WordPress.com Logo

You are commenting using your WordPress.com account. Log Out / Change )

Twitter picture

You are commenting using your Twitter account. Log Out / Change )

Facebook photo

You are commenting using your Facebook account. Log Out / Change )

Google+ photo

You are commenting using your Google+ account. Log Out / Change )

Connecting to %s