对Windows HANDLE使用std :: unique_ptr
Posted
tags:
篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了对Windows HANDLE使用std :: unique_ptr相关的知识,希望对你有一定的参考价值。
我试图使用std :: unique_ptrs以异常安全的方式管理Windows HANDLE。
首先我试过:
struct HandleDeleter
{
void operator()( HANDLE handle )
{
if( handle )
{
FindVolumeClose( handle )
}
}
}
typedef std::unique_ptr< HANDLE, HandleDeleter > unique_vol_handle_t;
稍后在我的代码中尝试使用它时:
unique_vol_handle_t volH( FindFirstVolumeW( buffer, MAX_GUID_PATH ) );
我从Visual Studio 2012RC收到以下错误:
1> error C2664: 'std::unique_ptr<_Ty,_Dx>::unique_ptr(std::nullptr_t) throw()' : cannot convert parameter 1 from 'HANDLE' to 'std::nullptr_t'
1> with
1> [
1> _Ty=HANDLE,
1> _Dx=VolumeHandleDeleter
1> ]
1> nullptr can only be converted to pointer or handle types
引用上面的volH声明行。
搜索了一段时间后,我发现a blog article基本上说,添加:
typedef HANDLE pointer;
到结构声明的顶部,一切都会很好。
我不相信,但我尝试了它,它确实解决了错误。我很困惑如何定义一个类型(甚至没有引用它)可以产生这样的差异。
两个问题:
1)你能解释原始错误吗?我不明白为什么编译器指的是std::nullptr_t/nullptr
。
2)typedef如何解决这个问题(或者至少看起来如此)?是否有一个较少的“远距离的幽灵行动”解决方案?
unique_ptr
的实施检查了删除器上是否存在::pointer
类型。如果删除器具有::pointer
类型,则此类型用作pointer
上的unique_ptr
typedef。否则,使用指向第一个模板参数的指针。
根据cppreference.com,unique_ptr::pointer
类型定义为
如果那种类型存在,
std::remove_reference<D>::type::pointer
,否则T*
存储的指向自有资源的指针
stored_ptr
具有类型指针。如果定义则为Del::pointer
,如果没有则为Type *
。如果删除器是无状态的,则存储的删除器对象stored_deleter
在对象中不占用空间。请注意,Del可以是引用类型。
这意味着如果你提供一个删除函子,它必须提供一个pointer
类型,用于unique_ptr
的实际指针类型。否则它将是指向您提供的类型的指针,在您的情况下HANDLE*
不正确。
我建议你看看A Proposal to Add additional RAII Wrappers to the Standard Library和the drawbacks of using smart pointers with handles。
就个人而言,我正在利用该提案的参考实现,而不是在这些情况下使用std::unique_ptr
。
我一直在为Windows中的各种类型的句柄执行以下操作。假设我们已在某处声明:
std::unique_ptr<void, decltype (&FindVolumeClose)> fv (nullptr, FindVolumeClose);
这包括:
HANDLE temp = FindFirstVolume (...);
if (temp != INVALID_HANDLE_VALUE)
fv.reset (temp);
无需声明单独的结构来包装删除器。由于HANDLE
真的是void *
,unique_ptr
将void
作为其类型;对于使用DECLARE_HANDLE
宏的其他类型的句柄,这可以避免:
// Manages the life of a HHOOK
std::unique_ptr<HHOOK__, decltype (&UnhookWindowsHookEx)> hook (nullptr, UnhookWindowsHookEx);
等等。
使用std::unique_ptr
for Windows HANDLEs的正确(和安全)方式是这样的:
#include <windows.h>
#include <memory>
class WinHandle {
HANDLE value_;
public:
WinHandle(std::nullptr_t = nullptr) : value_(nullptr) {}
WinHandle(HANDLE value) : value_(value == INVALID_HANDLE_VALUE ? nullptr : value) {}
explicit operator bool() const { return value_ != nullptr; }
operator HANDLE() const { return value_; }
friend bool operator ==(WinHandle l, WinHandle r) { return l.value_ == r.value_; }
friend bool operator !=(WinHandle l, WinHandle r) { return !(l == r); }
struct Deleter {
typedef WinHandle pointer;
void operator()(WinHandle handle) const { CloseHandle(handle); }
};
};
inline bool operator ==(HANDLE l, WinHandle r) { return WinHandle(l) == r; }
inline bool operator !=(HANDLE l, WinHandle r) { return !(l == r); }
inline bool operator ==(WinHandle l, HANDLE r) { return l == WinHandle(r); }
inline bool operator !=(WinHandle l, HANDLE r) { return !(l == r); }
typedef std::unique_ptr<WinHandle, WinHandle::Deleter> HandlePtr;
这样INVALID_HANDLE_VALUE
被隐式地视为null,因此永远不会传递给CloseHandle
函数,你永远不需要显式测试它。正如您通常所做的那样,使用std::unique_ptr
的operator bool()
:
HandlePtr file(CreateFile(...));
if (!file) {
// handle error
}
编辑:我最初忘记INVALID_HANDLE_VALUE
is not the only invalid value为HANDLE
类型,并且实际上它被许多(如果不是大多数)内核函数视为valid pseudo handle。嗯,很高兴知道。
虽然@Levi Haskell的回答考虑了INVALID_HANDLE_VALUE,但它没有考虑0是有效的句柄值并将INVALID_HANDLE_VALUE和0(或nullptr)视为相同。这不正确:
我的建议(基于Levi Haskell,他的代码因此归功于他)
template<void *taInvalidHandleValue>
class cWinHandle
{
HANDLE value_ { taInvalidHandleValue };
public:
cWinHandle() = default;
cWinHandle(HANDLE value) : value_(value) {}
~cWinHandle()
{
reset();
}
explicit operator bool() const { return value_ != taInvalidHandleValue; }
operator HANDLE() const { return value_; }
HANDLE get() const { return value_; }
HANDLE release() { HANDLE const result{ value_ }; value_ = taInvalidHandleValue; return result; }
friend bool operator ==(cWinHandle l, cWinHandle r) { return l.value_ == r.value_; }
friend bool operator !=(cWinHandle l, cWinHandle r) { return !(l == r); }
void reset ()
{
if (value_ != taInvalidHandleValue)
{
CloseHandle(value_);
value_ = taInvalidHandleValue;
}
}
};
inline bool operator ==(HANDLE l, cWinHandle<nullptr> r) { return cWinHandle<nullptr>(l) == r; }
inline bool operator !=(HANDLE l, cWinHandle<nullptr> r) { return !(l == r); }
inline bool operator ==(cWinHandle<nullptr> l, HANDLE r) { return l == cWinHandle<nullptr>(r); }
inline bool operator !=(cWinHandle<nullptr> l, HANDLE r) { return !(l == r); }
inline bool operator ==(HANDLE l, cWinHandle<INVALID_HANDLE_VALUE> r) { return cWinHandle<INVALID_HANDLE_VALUE>(l) == r; }
inline bool operator !=(HANDLE l, cWinHandle<INVALID_HANDLE_VALUE> r) { return !(l == r); }
inline bool operator ==(cWinHandle<INVALID_HANDLE_VALUE> l, HANDLE r) { return l == cWinHandle<INVALID_HANDLE_VALUE>(r); }
inline bool operator !=(cWinHandle<INVALID_HANDLE_VALUE> l, HANDLE r) { return !(l == r); }
以上是关于对Windows HANDLE使用std :: unique_ptr的主要内容,如果未能解决你的问题,请参考以下文章