devdimi
devdimi

Reputation: 2462

How to use C++ standard smart pointers with Windows HANDLEs?

I was wondering if there is a way to use unique_ptr<T> with Windows HANDLEs?

I was thinking to replace the std::default_delete with specific handle_trats that calls CloseHandle. The problem is that HANDLE is defined as void* unique_ptr<void> won't compile as sizeof(void) is not defined.

So far I see only two possibilities:

  1. Create a wrapper class for HANDLEs and use it like this: unique_ptr<new CHandle(h)>. This pretty much makes the unique_ptr<T> itself useless.
  2. Use HANDLE specific smart pointer class that resembles unique_ptr<T>.

What do you think is better choice? What would you suggest?

The question can be extended for COM IUnknown pointers - can CComPtr be replaced by any of the standard smart pointers?

Upvotes: 10

Views: 7620

Answers (10)

WBuck
WBuck

Reputation: 5503

Here is a handy little "custom_ptr" I use for dealing with a HANDLE. C++17 is required in order for this to compile. This idea was lifted from another SO post, I only added the std::remove_pointer_t<T> so that I wouldn't have to pass void as the custom_ptr type parameter.

Definition

#include <type_traits>
#include <memory>

template<auto fn>
using deleter_from_fn = std::integral_constant<decltype( fn ), fn>;

template<typename T, auto fn>
using custom_ptr = std::unique_ptr<typename std::remove_pointer_t<T>, deleter_from_fn<fn>>;

Use

custom_ptr<HANDLE, &CloseHandle> phandle{
    CreateFileMapping( INVALID_HANDLE_VALUE, nullptr, PAGE_READWRITE, 0, 1024, L"FileName" ) };

Upvotes: 0

Michael Chourdakis
Michael Chourdakis

Reputation: 11178

A HANDLE does not always close with CloseHandle(), beware. For example a HANDLE opened with FindNextFile() must be closed by FindClose().

Upvotes: 2

Roman Kruglov
Roman Kruglov

Reputation: 3547

inspired by Alexander Drichel's solution, here is even shorter

std::unique_ptr< HANDLE, decltype(&CloseHandle) > uniqueHandle( nullptr, CloseHandle );

Works in MSVC 2010. Note that you need to specify '&' for the function name in decltype() to deduce a pointer-to-function type.

Upvotes: 3

Seva Alekseyev
Seva Alekseyev

Reputation: 61378

Create a specific smart pointer class, won't take long. Don't abuse library classes. Handle semantics is quite different from that of a C++ pointer; for one thing, dereferencing a HANDLE makes no sense.

One more reason to use a custom smart handle class - NULL does not always mean an empty handle. Sometimes it's INVALID_HANDLE_VALUE, which is not the same (actually -1).

Upvotes: 7

Alexander Drichel
Alexander Drichel

Reputation: 515

yet another solution

std::unique_ptr< void, void(*)( HANDLE ) > uniqueHandle( file, []( HANDLE h ) { ::CloseHandle( h ); } );

Upvotes: 3

oblitum
oblitum

Reputation: 12016

I don't recommend using smart pointers with handles.

I recommend you to take a look at A Proposal to Add additional RAII Wrappers to the Standard Library and at the drawbacks of using smart pointers with handles.

Personally, I'm making use of the reference implementation of that proposal instead of using std::unique_ptr for these situations.

Upvotes: 1

mkaes
mkaes

Reputation: 14119

You can typedef your unique_ptr with a custom deleter

struct handle_deleter
{
    void operator()(void* handle)
    {
        if(handle != nullptr)
            CloseHandle(handle);
    }
};

typedef std::unique_ptr<void, handle_deleter> UniqueHandle_t;
UniqueHandle_t ptr(CreateFile(...));

Upvotes: 3

Puppy
Puppy

Reputation: 146968

The question can be extended for COM IUnknown pointers - can CComPtr be replaced by any of the standard smart pointers?

Yes. You don't specialize std::default_deleter, you simply replace the deleter type.

struct COMDeleter {
    template<typename T> void operator()(T* ptr) {
        ptr->Release();
    }
};
unique_ptr<IUnknown, COMDeleter> ptr; // Works fine

The same principle applies to shared_ptr and indeed, to HANDLE.

Upvotes: 9

Yochai Timmer
Yochai Timmer

Reputation: 49261

You can create a Deleter class that will release the handle instead of calling delete().

You can see in this LINK how they've solved deleting arrays with a shared_ptr (unique_ptr also has a constructor that recieves a Delete class)

  struct handle_deleter
  {   
    void operator ()( HANDLE handle)
      { CloseHandle(p); }
  };

  HANDLE blah = GetSomeHandle();
  unique_ptr myPointer(blah,handle_deleter);

Upvotes: 1

Alex
Alex

Reputation: 5439

You must use CloseHandle() to "close" a handle instead using delete. They will be deleted by Windows as soon as they are not opened elsewhere. So you could write a wrapper that calls CloseHandle() instead of deleteing it. You could also write your own smartpointer class which might be better as there is no need of a wrapper anymore.

Upvotes: 0

Related Questions