Making a HANDLE RAII-compliant using shared_ptr with a custom deleter

前端 未结 4 948
广开言路
广开言路 2021-01-02 04:35

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 typedeff

相关标签:
4条回答
  • 2021-01-02 05:04

    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).
    0 讨论(0)
  • 2021-01-02 05:09

    Take a look at boost 2: shared_ptr wraps resource handles

    0 讨论(0)
  • 2021-01-02 05:27

    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;
    };
    
    0 讨论(0)
  • 2021-01-02 05:28

    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.

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