如何在不创建新int的情况下设计RAII文件描述符

时间:2014-03-01 21:16:09

标签: c++ c++11 file-descriptor raii

我想在文件描述符周围创建一个RAII包装器。由于对象可能围绕线程传递,它实际上是一个共享资源:这就是我使用shared_ptr和自定义析构函数进行第一次实现的原因。

struct file_descriptor
{
    file_descriptor( const std::string & pathname, int flags )
        :m_fd( initialize( pathname, flags ) )
    {
    }

    file_descriptor( const int opened_fd )
        :m_fd( initialize( opened_fd ) )
    {
    }

    operator int() const { return *m_fd; }

private:
    std::shared_ptr<int> initialize( const int opened_fd )
    {
        std::shared_ptr<int> ptr_to_fd;

        try
        {
            int * shared_fd = new int;
            ptr_to_fd = std::shared_ptr<int>( shared_fd, file_descriptor_closer() );
            *shared_fd = opened_fd;
        }
        catch( std::bad_alloc & )
        {
            close( opened_fd );
            throw;
        }

        return ptr_to_fd;
    }

    std::shared_ptr<int> initialize( const std::string & pathname, int flags )
    {
        const int fd = open( pathname.c_str(), flags );        
        if (fd < 0)
            throw std::system_error( std::error_code(errno, std::system_category() ), "cannot create file descriptor" );

        return initialize( fd );
    }
    std::shared_ptr<int> m_fd;
};

自定义析构函数非常简单:

struct file_descriptor_closer
{
    void operator()(int * const fd) noexcept { if (fd) close(*fd); delete fd; }
}; 

现在我发现设计很糟糕,即因为“新的int”。我想过将自定义分配器指向已经分配的块,但这似乎有点过分了。你们有建议简化这个吗?

3 个答案:

答案 0 :(得分:4)

恕我直言,你是混合责任。让你的RAII类处理文件描述符的打开和关闭。让其他课程处理你的RAII课程的终身问题。正如您现在所拥有的那样,您的file_descriptor类的用户需要知道它在内部使用shared_ptr。乍一看,如果我要在线程之间共享一个file_descriptor,我会自己制作一个shared_ptr<file_descriptor>来解决我不知道内部已经在做的问题。

答案 1 :(得分:1)

使用一些温和的暴力:

struct file_descriptor_closer
{
    void operator()(void* fd) noexcept { if (fd) close(reinterpret_cast< int >(fd)); }
}; 
struct file_descriptor
{
    file_descriptor( const std::string & pathname, int flags )
        :m_fd( initialize( pathname, flags ) )
    {
    }

    file_descriptor( const int opened_fd )
        :m_fd( initialize( opened_fd ) )
    {
    }

    operator int() const { return reinterpret_cast< int >(m_fd.get()); }

private:
    std::shared_ptr<void> initialize( const int opened_fd )
    {
        try
        {
            return std::shared_ptr< void >( reinterpret_cast< void* >( opened_fd ), file_descriptor_closer() );
        }
        catch( std::bad_alloc & )
        {
            close( opened_fd );
            throw;
        }
    }

    std::shared_ptr<void> initialize( const std::string & pathname, int flags )
    {
        const int fd = open( pathname.c_str(), flags );        
        if (fd < 0)
            throw std::system_error( std::error_code(errno, std::system_category() ), "cannot create file descriptor" );

        return initialize( fd );
    }
    std::shared_ptr<void> m_fd;
};

答案 2 :(得分:-1)

为什么不创建自己的容器?类似于:http://ideone.com/m3kmaJ或静态计数器:http://ideone.com/Gs4Kb7

#include <iostream>
#include <sys/stat.h>
#include <fcntl.h>
#include <thread>
#include <memory>
#include <unistd.h>
#include <atomic>

class FD
{
    private:
        int fd;
        static int count;

    public:
        FD(const char* FilePath, int flags) : fd(open(FilePath, flags)) {++FD::count;}
        FD(const FD& other) : fd(other.fd) {++FD::count;}
        FD(FD&& other) : fd(other.fd) { other.fd = -1; }

        ~FD()
        {
            FD::count -= 1;
            if (FD::count == 0)
            {
                std::cout<<"Destroyed\n";
                if (is_open())
                    close(fd);
            }
        }

        bool is_open() {return fd != -1;}
        FD* operator &() {return nullptr;}
        operator int() {return fd;}

        FD& operator = (FD other)
        {
            fd = other.fd;
            FD::count += 1;
            return *this;
        }

        FD& operator = (FD&& other)
        {
            fd = other.fd;
            other.fd = -1;
            return *this;
        }
};

int FD::count = 0;

int main()
{
    FD fd = FD("Unicode.cpp", O_RDONLY);
    FD copy = fd;
    FD cpy = FD(copy);

    return 0;
}