使用带有自定义删除器的shared_ptr使HANDLE RAII兼容

发布于 2024-08-07 20:38:38 字数 1360 浏览 8 评论 0原文

我最近在 SO 上发布了有关 RAII 的一般问题。 但是,我的 HANDLE 示例仍然存在一些实现问题。

HANDLEwindows.h 中被类型定义为 void *。因此,正确的 shared_ptr 定义必须是

std::tr1::shared_ptr<void> myHandle (INVALID_HANDLE_VALUE, CloseHandle);

示例 1 CreateToolhelp32Snapshot:返回 HANDLE 并有效。

const std::tr1::shared_ptr<void> h
    (CreateToolhelp32Snapshot(TH32CS_SNAPPROCESS, NULL), CloseHandle);

当我在定义中使用 void (正确的方法是什么?)时,当我尝试使用此指针调用更多 winapi 命令时,问题仍在继续。它们功能正常,但很丑,我确信必须有更好的解决方案。

在以下示例中,h 是通过顶部定义创建的指针。

示例 2 OpenProcessToken:最后一个参数是 PHANDLE。与演员阵容中等丑陋。

OpenProcessToken(GetCurrentProcess(), TOKEN_ADJUST_PRIVILEGES | TOKEN_QUERY,
    (PHANDLE)&h);

示例 3 Process32First:第一个参数是 HANDLE。真的很难看。

Process32First(*((PHANDLE)&h), &pEntry);

示例 4 与常量 HANDLE 的简单比较。真的很难看。

if (*((PHANDLE)&h) == INVALID_HANDLE) { /* do something */ }

为 HANDLE 创建正确的共享指针的正确方法是什么?

I've recently posted a general question about RAII at SO.
However, I still have some implementation issues with my HANDLE example.

A HANDLE is typedeffed to void * in windows.h. Therefore, the correct shared_ptr definition needs to be

std::tr1::shared_ptr<void> myHandle (INVALID_HANDLE_VALUE, CloseHandle);

Example 1 CreateToolhelp32Snapshot: returns HANDLE and works.

const std::tr1::shared_ptr<void> h
    (CreateToolhelp32Snapshot(TH32CS_SNAPPROCESS, NULL), CloseHandle);

As I use void in the definition (what is the correct way?) problems go on, when I try to call some more winapi commands with this pointer. They functionally work, but are ugly and I am sure that there has to be a better solution.

In the following examples, h is a pointer which was created via the definition at the top.

Example 2 OpenProcessToken: last argument is a PHANDLE. medium ugly with the cast.

OpenProcessToken(GetCurrentProcess(), TOKEN_ADJUST_PRIVILEGES | TOKEN_QUERY,
    (PHANDLE)&h);

Example 3 Process32First: first argument is a HANDLE. REALLY ugly.

Process32First(*((PHANDLE)&h), &pEntry);

Example 4 simple comparison with a constant HANDLE. REALLY ugly.

if (*((PHANDLE)&h) == INVALID_HANDLE) { /* do something */ }

What is the correct way to create a proper shared_ptr for a HANDLE?

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(4

相守太难 2024-08-14 20:38:38

示例 1 正确,

示例 2 错误。通过盲目转换为 PHANDLE,shared_ptr 逻辑被绕过。它应该是这样的:

HANDLE h;
OpenProcessToken(...., &h);
shared_ptr<void> safe_h(h, &::CloseHandle);

或者,分配给预先存在的shared_ptr:

shared_ptr<void> safe_h = ....
{
  HANDLE h;
  OpenProcessToken(...., &h);
  safe_h.reset(h, &::CloseHandle);
}//For extra safety, limit visibility of the naked handle

或者,创建您自己的、安全的OpenProcessToken版本,它返回一个共享句柄而不是获取PHANDLE:

// Using SharedHandle defined at the end of this post
SharedHandle OpenProcess(....)
{
    HANDLE h = INVALID_HANDLE_VALUE;
    ::OpenProcessToken(...., &h);
    return SharedHandle(h);
}

示例3:不需要走这些弯路。这应该没问题:

Process32First(h.get(), ...);

示例 4:同样,不要绕道:

if (h.get() == INVALID_HANDLE){...}

为了让事情变得更好,您可以输入类似以下内容的 typedef:

typedef shared_ptr<void> SharedHandle;

或者更好的是,如果要使用 CloseHandle() 关闭所有句柄,请创建一个包含 Shared_ptr 的 SharedHandle 类并自动提供正确的删除器:

// Warning: Not tested. For illustration purposes only
class SharedHandle
{
public:
  explicit SharedHandle(HANDLE h) : m_Handle(h, &::CloseHandle){};
  HANDLE get()const{return m_Handle.get();}

  //Expose other shared_ptr-like methods as needed
  //...

private:
  shared_ptr<void> m_Handle;
};

Example 1 is OK

Example 2 is wrong. By blindly casting to PHANDLE, the shared_ptr logic is bypassed. It should be something like this instead:

HANDLE h;
OpenProcessToken(...., &h);
shared_ptr<void> safe_h(h, &::CloseHandle);

or, to assign to a pre-exising shared_ptr:

shared_ptr<void> safe_h = ....
{
  HANDLE h;
  OpenProcessToken(...., &h);
  safe_h.reset(h, &::CloseHandle);
}//For extra safety, limit visibility of the naked handle

or, create your own, safe, version of OpenProcessToken that returns a shared handle instead of taking a PHANDLE:

// Using SharedHandle defined at the end of this post
SharedHandle OpenProcess(....)
{
    HANDLE h = INVALID_HANDLE_VALUE;
    ::OpenProcessToken(...., &h);
    return SharedHandle(h);
}

Example 3: No need to take these detours. This should be ok:

Process32First(h.get(), ...);

Example 4: Again, no detour:

if (h.get() == INVALID_HANDLE){...}

To make things nicer, you could typedef something like:

typedef shared_ptr<void> SharedHandle;

or better yet, if all handles are to be closed with CloseHandle(), create a SharedHandle class wrapping a shared_ptr and automatically providing the right deleter:

// Warning: Not tested. For illustration purposes only
class SharedHandle
{
public:
  explicit SharedHandle(HANDLE h) : m_Handle(h, &::CloseHandle){};
  HANDLE get()const{return m_Handle.get();}

  //Expose other shared_ptr-like methods as needed
  //...

private:
  shared_ptr<void> m_Handle;
};
昵称有卵用 2024-08-14 20:38:38

不要为此而烦恼shared_ptr,使用ATL::CHandle。

原因如下:

  • 当您看到 CHandle 时,您就知道它是句柄的 RAII 包装器。
  • 当您看到 shared_ptr 时,您不知道它是什么。
  • CHandle 不会共享所有权(但是在某些情况下您可能需要共享所有权)。
  • CHandle 是 Windows 开发堆栈的标准。
  • CHandle 比带有自定义删除器的 shared_ptr 更紧凑(更少的打字/读取)。

Don't bother with shared_ptr for that, use ATL::CHandle.

Here is why:

  • When you see CHandle you know that it's a RAII wrapper for a handle.
  • When you see shared_ptr<void> you don't know what it is.
  • CHandle doesn't make an ownership shared (however in some cases you may want a shared ownership).
  • CHandle is a standard for a windows development stack.
  • CHandle is more compact than shared_ptr<void> with custom deleter (less typing/reading).
拍不死你 2024-08-14 20:38:38

这是我的替代方案,这非常好,除了您需要始终在 .get() 之后取消引用并且需要函子或 lambda:

template<typename HandleType, typename Deleter>
std::shared_ptr<HandleType> make_shared_handle(HandleType _handle, Deleter _dx)
{
    return std::shared_ptr<HandleType>(new HandleType(_handle), _dx);
}

然后:

auto closeHandleDeleter = [](HANDLE* h) {
    ::CloseHandle(*h);
    delete h;
};
std::shared_ptr<HANDLE> sp = make_shared_handle(a_HANDLE, closeHandleDeleter);
f_that_takes_handle(*sp.get());

我最喜欢的是没有额外的工作要做访问它:

std::weak_ptr<HANDLE> wp = sp; // Yes. This could make sense in some designs.

当然,辅助函数适用于任何类似的句柄类型。

Here is my alternative, which is quite nice except you need to dereference always after .get() and requires a functor or lambda:

template<typename HandleType, typename Deleter>
std::shared_ptr<HandleType> make_shared_handle(HandleType _handle, Deleter _dx)
{
    return std::shared_ptr<HandleType>(new HandleType(_handle), _dx);
}

then:

auto closeHandleDeleter = [](HANDLE* h) {
    ::CloseHandle(*h);
    delete h;
};
std::shared_ptr<HANDLE> sp = make_shared_handle(a_HANDLE, closeHandleDeleter);
f_that_takes_handle(*sp.get());

what I like most about this is there is no extra work to have access to this:

std::weak_ptr<HANDLE> wp = sp; // Yes. This could make sense in some designs.

and of course, the helper function works with any handle type of the likes.

~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文