10

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?

Ajay
  • 18,086
  • 12
  • 59
  • 105
devdimi
  • 2,432
  • 19
  • 18

10 Answers10

9

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.

Puppy
  • 144,682
  • 38
  • 256
  • 465
6

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).

Seva Alekseyev
  • 59,826
  • 25
  • 160
  • 281
  • 1
    I wrote my own set up HANDLE-specific classes, as some handles require `CloseHandle()` while others require different API-specific funtions, some HANDLEs are set to INVALID_HANDLE_VALUE when not assigned and others are set to NULL instead, etc. I wrote a set of base classes for the bulk of the work, and then use custom trait classes to handle closure and assignment issues. – Remy Lebeau Mar 23 '12 at 22:16
  • -1 for re-implementing library classes badly. They are explicitly designed for this functionality, it's not an abuse. You will introduce pointless new bugs and code. – Puppy Oct 09 '14 at 15:23
  • +1 for recognizing that pointers and HANDLEs are different things with different semantics. Shoehorning HANDLEs into a smart pointer type gives you a bunch of methods that don't really make sense for HANDLEs. – Adrian McCarthy Jan 27 '16 at 19:51
4

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.

Roman Kruglov
  • 3,375
  • 2
  • 40
  • 46
3

yet another solution

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

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(...));
mkaes
  • 13,781
  • 10
  • 52
  • 72
1

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.

Community
  • 1
  • 1
oblitum
  • 11,380
  • 6
  • 54
  • 120
1

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

Michael Chourdakis
  • 10,345
  • 3
  • 42
  • 78
1

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);
Yochai Timmer
  • 48,127
  • 24
  • 147
  • 185
0

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" ) };
WBuck
  • 5,162
  • 2
  • 25
  • 36
0

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.

Alex
  • 5,240
  • 1
  • 31
  • 38