std::unique_ptr, deleters and the Win32 API

前端 未结 3 1202
情书的邮戳
情书的邮戳 2020-12-01 07:57

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 w

相关标签:
3条回答
  • 2020-12-01 08:24

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

    0 讨论(0)
  • 2020-12-01 08:39

    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);
    
    0 讨论(0)
  • 2020-12-01 08:40

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

    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.

    0 讨论(0)
提交回复
热议问题