One-liner for RAII on non pointer?

前端 未结 6 1773
小蘑菇
小蘑菇 2020-12-24 08:31

Related topic

std::unique_ptr, deleters and the Win32 API

To use a Win32 Handle as a RAII, I can use the following line

std:         


        
6条回答
  •  感情败类
    2020-12-24 09:15

    It is well known the example to RAII a FILE* using std::unique_ptr:

    struct FILEDeleter
    {
        typedef FILE *pointer;
        void operator()(FILE *fp) { fclose(fp); }
    };
    
    typedef std::unique_ptr FilePtr;
    
    FilePtr f(fopen("file.txt", "r"));
    

    Alas, a similar approach to POSIX close() to RAII a file descriptor is not possible:

    struct FDDeleter
    {
        typedef int pointer;
        void operator()(int fd) { close(fp); }
    };
    
    typedef std::unique_ptr FD;
    

    Although some compilers will work just fine, it is not valid because the fd==0 is a valid file descriptor! The null one should be -1. But anyway, even if it were 0 it is still not valid, because FDDeleter::pointer shall satisfy the requirements of NullablePointer (summing up):

    1. It shall be comparable to nullptr.
    2. It shall be value-initialized to a value that compares equal to nullptr.

    Thus, UniqueHandle is born!

    #include 
    
    template 
    class UniqueHandle
    {
    public:
        UniqueHandle(std::nullptr_t = nullptr)
            :m_id(TNul)
        { }
        UniqueHandle(T x)
            :m_id(x)
        { }
        explicit operator bool() const { return m_id != TNul; }
    
        operator T&() { return m_id; }
        operator T() const { return m_id; }
    
        T *operator&() { return &m_id; }
        const T *operator&() const { return &m_id; }
    
        friend bool operator == (UniqueHandle a, UniqueHandle b) { return a.m_id == b.m_id; }
        friend bool operator != (UniqueHandle a, UniqueHandle b) { return a.m_id != b.m_id; }
        friend bool operator == (UniqueHandle a, std::nullptr_t) { return a.m_id == TNul; }
        friend bool operator != (UniqueHandle a, std::nullptr_t) { return a.m_id != TNul; }
        friend bool operator == (std::nullptr_t, UniqueHandle b) { return TNul == b.m_id; }
        friend bool operator != (std::nullptr_t, UniqueHandle b) { return TNul != b.m_id; }
    
    private:
        T m_id;
    };
    

    Its use is pretty easy, best seen with an example:

    struct FDDeleter
    {
        typedef UniqueHandle pointer;
        void operator()(pointer p)
        {
            close(p);
        }
    };
    typedef std::unique_ptr FD;
    
    FD fd(open("test.txt", O_RDONLY));
    

    If you truly want a one-liner you could go with this generalization:

    template 
    struct OLDeleter
    {
        typedef UniqueHandle pointer;
        void operator()(pointer p)
        {
            D(p);
        }
    };
    

    And then just one line:

    std::unique_ptr > FD fd(open("test.txt", O_RDONLY));
    

    The problem is that you must add the return of close() as a template argument and assume that there isn't anything funny about this function that prevents its conversion to a int(*)(int) (weird calling conventions, extra parameters, macros...) and that is quite inconvenient.

    You could add a function wrapper:

    void my_close(int fd) { close(fd); }
    

    But if you are into it, you could as well write the whole struct FDDeleter.

提交回复
热议问题