Using unique_ptr to control a file descriptor

前端 未结 9 2071
無奈伤痛
無奈伤痛 2020-12-10 05:29

In theory, I should be able to use a custom pointer type and deleter in order to have unique_ptr manage an object that is not a pointer. I tried the following c

相关标签:
9条回答
  • 2020-12-10 05:35

    This solution is based on Nicol Bolas answer:

    struct FdDeleter
    {
        typedef int pointer;
        void operator()(int fd)
        {
            ::close(fd);
        }
    };
    typedef std::unique_ptr<int, FdDeleter> UniqueFd;
    

    It's short, but you have to avoid to compare UniqueFd instance with nullptr and use it as boolean expression:

    UniqueFd fd(-1, FdDeleter()); //correct
    //UniqueFd fd(nullptr, FdDeleter()); //compiler error
    if (fd.get() != -1) //correct
    {
        std::cout << "Ok: it is not printed" << std::endl;
    }
    if (fd) //incorrect, avoid
    {
        std::cout << "Problem: it is printed" << std::endl;
    }
    if (fd != nullptr) //incorrect, avoid
    {
        std::cout << "Problem: it is printed" << std::endl;
    }
    return 1;
    
    0 讨论(0)
  • 2020-12-10 05:38

    I would suggest using shared_ptr rather than unique_ptr to manage the life time of int handles because the shared ownership semantics are usually a better fit, and because the type of the deleter is erased. You need the following helper:

    namespace handle_detail
    {
        template <class H, class D>
        struct deleter
        {
            deleter( H h, D d ): h_(h), d_(d) { }
            void operator()( H * h ) { (void) d_(h_); }
            H h_;
            D d_;
        };
    }
    
    template <class H,class D>
    std::shared_ptr<H const>
    make_handle( H h, D d )
    {
        std::shared_ptr<H> p((H *)0,handle_detail::deleter<H,D>(h,d));
        return std::shared_ptr<H const>(
            p,
            &std::get_deleter<handle_detail::deleter<H,D> >(p)->h_ );
    }
    

    To use with a file descriptor:

    int fh = open("readme.txt", O_RDONLY); // Check for errors though.
    std::shared_ptr<int const> f = make_handle(fh, &close);
    
    0 讨论(0)
  • 2020-12-10 05:41

    Can you do something simple like the following?

    class unique_fd {
    public:
        unique_fd(int fd) : fd_(fd) {}
        unique_fd(unique_fd&& uf) { fd_ = uf.fd_; uf.fd_ = -1; }
        ~unique_fd() { if (fd_ != -1) close(fd_); }
    
        explicit operator bool() const { return fd_ != -1; }
    
    private:
        int fd_;
    
        unique_fd(const unique_fd&) = delete;
        unique_fd& operator=(const unique_fd&) = delete;
    };
    

    I do not see why you had to use unique_ptr, which is designed to manage pointers.

    0 讨论(0)
  • 2020-12-10 05:42

    A complete sample:

    #include <memory>
    #include <unistd.h>
    #include <fcntl.h>
    
    template<class T, T nullvalue, class D, D d> // auto d works in new compilers.
    struct generic_delete
    {
        class pointer
        {
            T t;
        public:
            pointer(T t) : t(t) {}
            pointer(std::nullptr_t = nullptr) : t(nullvalue) { }
            explicit operator bool() { return t != nullvalue; }
            friend bool operator ==(pointer lhs, pointer rhs) { return lhs.t == rhs.t; }
            friend bool operator !=(pointer lhs, pointer rhs) { return lhs.t != rhs.t; }
            operator T() { return t; }
        };
        void operator()(pointer p)
        {
            d(p);
        }
    };
    using unique_fd = std::unique_ptr<void, generic_delete<int, -1, decltype(&close), &close>>;
    static_assert(sizeof(unique_fd) == sizeof(int), "bloated unique_fd");
    
    int main()
    {
        unique_fd fd1(open("fd.txt", O_WRONLY | O_CREAT | O_TRUNC, S_IRUSR | S_IWUSR));
        write(fd1.get(), "hello\n", 6);
    }
    
    0 讨论(0)
  • 2020-12-10 05:43

    Don't coerce a (smart) pointer to be a non-pointer object.

    In theory, I should be able to use a custom pointer type and deleter in order to have unique_ptr manage an object that is not a pointer.

    No, you should not. That is, in terms of getting it to compile and run, maybe, but you simply shouldn't use a unique_ptr to manage something which is not a pointer. You absolutely should write an appropriate RAII class for your resource - e.g. an OS file descriptor - or use an existing such class from some library. Only if you want a pointer to such a resource does a unique_ptr make sense; but then, you don't need a custom deleter.

    0 讨论(0)
  • 2020-12-10 05:43

    Making Nicol Bolas class more general:

    template<class T=void*,T null_val=nullptr>
    class Handle
        {
        public:
            Handle(T handle):m_handle(handle){}
    
            Handle(std::nullptr_t):m_handle(null_val){}
    
            operator T(){return m_handle;}
    
            bool operator==(const Handle& other) const
                {return other.m_handle==m_handle;}
    
        private:
            T m_handle;
        };
    
    typedef Handle<int,-1> FileDescriptor;
    typedef Handle<GLuint,0> GlResource; // according to http://stackoverflow.com/questions/7322147/what-is-the-range-of-opengl-texture-id
    // ...
    

    I am not sure if I should have default template parameter values or not.

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