26

In VC2012, I want to create a mutex in a constructor using a unique pointer and a deleter, so that I don't need to create a destructor just to call CloseHandle.

I would have thought that this would work:

struct foo
{
    std::unique_ptr<HANDLE, BOOL(*)(HANDLE)> m_mutex;
    foo() : m_mutex(CreateMutex(NULL, FALSE, NULL), CloseHandle) {}
}

but on compiling I get an error:

error C2664: 'std::unique_ptr<_Ty,_Dx>::unique_ptr(void *,int 
(__cdecl *const &)(HANDLE)) throw()' : cannot convert parameter 1 from 
'HANDLE' to 'void *'

When I modify the constructor thus:

foo() : m_mutex((void*)CreateMutex(NULL, FALSE, 
    (name + " buffer mutex").c_str()), CloseHandle) {}

I get the even more unusual:

error C2664: 'std::unique_ptr<_Ty,_Dx>::unique_ptr(void *,
int (__cdecl *const &)(HANDLE)) throw()' : cannot convert 
parameter 1 from 'void *' to 'void *'

I'm at a loss now. HANDLE is a typedef for void*: is there some conversion magic I need to know about?

xcdemon05
  • 1,372
  • 5
  • 25
  • 49
hatcat
  • 1,876
  • 1
  • 18
  • 37
  • There is a good answer to your question below, but I would seriously consider rolling my own custom owner class for a Mutex rather than exploiting std::unique_ptr's deleter for this purpose. The fact that a HANDLE is a pointer is an implementation detail. It could just as easily have been an index or other magic value. Make your own RAII wrapper for it, and leave unique_ptr to manage "real" pointers. – Adrian McCarthy Feb 12 '13 at 21:05
  • @Adrian: I see your point. It could also contain the RAII Wait/Release pairing in the constructor/destructor. Cheers. – hatcat Feb 12 '13 at 21:33

3 Answers3

50

Forget about the custom deleter for now. When you say std::unique_ptr<T>, the unique_ptr constructor expects to receive a T*, but CreateMutex returns a HANDLE, not a HANDLE *.

There are 3 ways to fix this:

std::unique_ptr<void, deleter> m_mutex;

You'll have to cast the return value of CreateMutex to a void *.

Another way to do this is use std::remove_pointer to get to the HANDLE's underlying type.

std::unique_ptr<std::remove_pointer<HANDLE>::type, deleter> m_mutex;

Yet another way to do this is to exploit the fact that if the unique_ptr's deleter contains a nested type named pointer, then the unique_ptr will use that type for its managed object pointer instead of T*.

struct mutex_deleter {
  void operator()( HANDLE h ) 
  {
    ::CloseHandle( h );
  }
  typedef HANDLE pointer;
};
std::unique_ptr<HANDLE, mutex_deleter> m_mutex;
foo() : m_mutex(::CreateMutex(NULL, FALSE, NULL), mutex_deleter()) {}

Now, if you want to pass a pointer to function type as the deleter, then when dealing with the Windows API you also need to pay attention to the calling convention when creating function pointers.

So, a function pointer to CloseHandle must look like this

BOOL(WINAPI *)(HANDLE)

Combining all of it,

std::unique_ptr<std::remove_pointer<HANDLE>::type, 
                BOOL(WINAPI *)(HANDLE)> m_mutex(::CreateMutex(NULL, FALSE, NULL),
                                                &::CloseHandle);

I find it easier to use a lambda instead

std::unique_ptr<std::remove_pointer<HANDLE>::type, 
                void(*)( HANDLE )> m_mutex;
foo() : m_mutex(::CreateMutex(NULL, FALSE, NULL), 
                []( HANDLE h ) { ::CloseHandle( h ); }) {}

Or as suggested by @hjmd in the comments, use decltype to deduce the type of the function pointer.

std::unique_ptr<std::remove_pointer<HANDLE>::type, 
                decltype(&::CloseHandle)> m_mutex(::CreateMutex(NULL, FALSE, NULL),
                                                  &::CloseHandle);
Praetorian
  • 106,671
  • 19
  • 240
  • 328
  • 6
    +1 Instead of the function pointer just use decltype(&CloseHandle) – hmjd Feb 12 '13 at 20:53
  • 1
    A lambda expression yields a value, not a type. I don't see how the lambda example will compile. – GManNickG Feb 12 '13 at 21:04
  • Blimey, I only nipped down for a spot of dinner. I love this site. Thanks for an excellent answer. – hatcat Feb 12 '13 at 21:34
  • 1
    As well as using `decltype` to deduce the type of the function pointer, you can initialize it with `std::ptr_fun(CloseHandle)` and avoid the lambda syntax. – Mahmoud Al-Qudsi Feb 12 '13 at 23:12
  • @MahmoudAl-Qudsi You don't need a lambda or `ptr_fun`, `&::CloseHandle` works. – Praetorian Feb 12 '13 at 23:15
  • @Praetorian Yes, true. (btw, referring to initialization of m_mutex, not the declaration of it.) May I suggest updating your answer to reflect that? – Mahmoud Al-Qudsi Feb 12 '13 at 23:17
  • Doesn't compile with Visual C++ 2013 with Update 4. – John Leidegren Mar 09 '15 at 17:53
  • @JohnLeidegren Which part doesn't compile on VS2013? – Praetorian Mar 09 '15 at 18:02
  • I get the following error `unique_ptr constructed with null deleter pointer` if I try to use it like this `typedef std::unique_ptr::type, decltype(&::CloseHandle)> handle_ptr;` maybe that improper use from my part but given the solution to just use a custom deleter with a specific `typedef HANDLE pointer;` member works fine, I'm going to use that. – John Leidegren Mar 09 '15 at 18:20
  • @JohnLeidegren That was my screw up, thanks for pointing it out. The [`unique_ptr` constructor](http://en.cppreference.com/w/cpp/memory/unique_ptr/unique_ptr) that takes a `T *` argument will value initialize the deleter, and if you're using a pointer to function type as the deleter, you end up with a `nullptr`. The updated code should work, but I'd just stick with the custom deleter with the appropriate nested `pointer` type. – Praetorian Mar 09 '15 at 18:37
  • 1
    Note for readers: this code is only able to work because `HANDLE` is an alias for a pointer type (probably `void *`). Similar code does not work for integer handles, because the pointer type of the unique_ptr must satisfy *NullablePointer*. – M.M May 07 '18 at 11:47
35

Others have pointed out how the whole HANDLE/HANDLE* issue works. Here's a much cleverer way to deal with it, using interesting features of std::unique_ptr.

struct WndHandleDeleter
{
  typedef HANDLE pointer;

  void operator()(HANDLE h) {::CloseHandle(h);}
};

typedef std::unique_ptr<HANDLE, WndHandleDeleter> unique_handle;

This allows unique_handle::get to return HANDLE instead of HANDLE*, without any fancy std::remove_pointer or other such things.

This works because HANDLE is a pointer and therefore satisfies NullablePointer.

Nicol Bolas
  • 449,505
  • 63
  • 781
  • 982
  • I'm slightly torn: I like the final line of Praetorian's answer as the whole solution is local, but I like your solution as it's a good piece of reuse. I'll give you a +1! Thanks. – hatcat Feb 12 '13 at 21:37
  • @MahmoudAl-Qudsi: I don't know why you would want to, but I suppose you could declare a struct with no name, but with a variable declaration. Then use `decltype` to get its type. But you're not gaining anything, since either way you're inserting a name into the namespace. – Nicol Bolas Feb 13 '13 at 01:01
  • @Nicol I'm thinking of a one-liner that is based off of an anonymous struct built into a templated alias. – Mahmoud Al-Qudsi Feb 13 '13 at 01:09
  • I am not seeing this work: http://liveworkspace.org/code/1etJg3$12 -- and a quick read of the `unique_ptr` specs over at http://en.cppreference.com/w/cpp/memory/unique_ptr/unique_ptr doesn't lead me to think that `Deleter::type::pointer` is used for `unique_ptr::pointer`. Is both gcc 4.7.2 and cppreference wrong? – Yakk - Adam Nevraumont Feb 13 '13 at 03:07
  • @Yakk: The actual unique_ptr page on that site [clearly shows where `pointer` comes from](http://en.cppreference.com/w/cpp/memory/unique_ptr#Member_types). As for your live workspace issue, that sounds very much like a libstdc++ bug. – Nicol Bolas Feb 13 '13 at 03:50
  • @Yakk: See my edit. I forgot that the `::type` is part of the template metafunction and not actually in the deleter's type. Now that makes much more sense. And the workspace compiles now. http://liveworkspace.org/code/1etJg3$13 – Nicol Bolas Feb 13 '13 at 03:56
  • 1
    +1 This is a very non-cryptic, easy to understand, solution, and the one I ended up using in my day-to-day make-Windows-behave header file. Thank you very much, Nicol. – WhozCraig Oct 03 '13 at 18:46
  • 4
    +1. Also should mention that the size of this unique_ptr is the size of a pointer (because this uses a stateless deleter), unlike Praetorian's final answer which is the size of 2 pointers. – Jon May 29 '14 at 01:57
  • ALso, "This works because HANDLE is a pointer and therefore satisfies NullablePointer" raises the question: What should you do for handles that don't satisfy NullablePointer? Write your own RAII or write an adaptor so that it satisfies NullablePointer? – Jon May 29 '14 at 02:02
  • 5
    Not all `HANDLE`-based APIs use `NULL`/`nullptr` to represent an invalid handle. Some use `INVALID_HANDLE_VALUE` (-1) instead. – Remy Lebeau Mar 09 '15 at 19:33
2

The problem is you actually define unque_ptr that holds pointer to handle (HANDLE*) type, but you pass just HANDLE, not pointer to it.

zen-cat
  • 425
  • 2
  • 9