std :: unique_ptr,deleters和Win32 API [英] std::unique_ptr, deleters and the Win32 API
问题描述
在VC2012中,我想使用一个唯一的指针和一个删除器在一个构造函数中创建一个互斥,所以我不需要创建一个析构函数来调用CloseHandle。
我会认为这会工作:
struct foo
{
std: :unique_ptr< HANDLE,BOOL(*)(HANDLE)> m_mutex;
foo():m_mutex(CreateMutex(NULL,FALSE,NULL),CloseHandle){}
}
但是在编译时我得到一个错误:
错误C2664:'std :: unique_ptr& _Dx> :: unique_ptr(void *,int
(__cdecl * const&)(HANDLE))throw()':不能将参数1从
'HANDLE'转换为'void *'
当我修改构造函数时:
foo():m_mutex(void *)CreateMutex(NULL,FALSE,
(name +buffer mutex)。c_str()),CloseHandle){}
我得到更不寻常的:
code> error C2664:'std :: unique_ptr< _Ty,_Dx> :: unique_ptr(void *,
int(__cdecl * const&)(HANDLE))throw() $ b参数1从'void *'到'void *'
。 HANDLE是void *的typedef:是否有一些转换魔法我需要知道?
现在。当您说 std :: unique_ptr< T>
时, unique_ptr
构造函数期望接收 T *
,但 CreateMutex
会返回 HANDLE
,而不是 HANDLE *
。
有3种方法可以解决此问题:
std :: unique_ptr< void,deleter> m_mutex;
您必须强制转换 CreateMutex
至 void *
。
另一种方法是使用 std :: remove_pointer
以访问 HANDLE
的底层类型。
std :: unique_ptr< std :: remove_pointer< HANDLE> :: type,deleter& m_mutex;
另一种方法是利用事实:如果 unique_ptr
的删除程序包含名为指针
的嵌套类型,则 unique_ptr
将使用该类型对于其管理对象指针,而不是 T *
。
struct mutex_deleter {
void operator()(HANDLE h)
{
:: CloseHandle(h);
}
typedef HANDLE指针;
};
std :: unique_ptr< HANDLE,mutex_deleter> m_mutex;
foo():m_mutex(:: CreateMutex(NULL,FALSE,NULL),mutex_deleter()){}
b $ b
现在,如果你想传递一个指向函数类型的指针作为删除器,那么当处理Windows API时,你还需要注意调用约定
因此,一个指向 CloseHandle
必须如下所示
BOOL(WINAPI *)(HANDLE)
std :: unique_ptr< std :: remove_pointer< HANDLE> :: type,
BOOL )(HANDLE)> m_mutex(:: CreateMutex(NULL,FALSE,NULL),
& :: CloseHandle);
我发现使用lambda比较容易。
std :: unique_ptr< std :: remove_pointer< HANDLE> :: type,
void(*)(HANDLE)> m_mutex;
foo():m_mutex(:: CreateMutex(NULL,FALSE,NULL),
[](HANDLE h){:: CloseHandle(h);}){}
或者按照@hjmd在注释中的建议,使用 decltype
函数指针的类型。
std :: unique_ptr< std :: remove_pointer< HANDLE> :: type,
decltype(& :: CloseHandle)> m_mutex(:: CreateMutex(NULL,FALSE,NULL),
& :: CloseHandle);
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?
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);
这篇关于std :: unique_ptr,deleters和Win32 API的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!