Вопрос

I want to create a RAII wrapper around a file descriptor. As the object might be passed around threads, it really is a shared resource: this is why I made a first implementation by using a shared_ptr with a custom destructor.

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;
};

The custom destructor, is pretty simple:

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

Now I find the design horrible, namely because of the "new int". I thought about making a custom allocator to point to an already-allocated block, but that seems overkill. Do you guys have suggestion to simplify this?

Это было полезно?

Решение

IMHO, you're mixing responsibilities. Let your RAII class deal with the opening and closing of the file descriptor. Let some other class deal with the lifetime question of your RAII class. As you have it now, the user of your file_descriptor class would need to know that it is using a shared_ptr internally. On first glance, if I were to share a file_descriptor between threads, I'd be making a shared_ptr<file_descriptor> of my own to counter the problem that I don't really know that internally it's already doing one.

Другие советы

use some gentle violence:

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;
};

Why not create your own container? What about something like: http://ideone.com/m3kmaJ or with static counter: 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;
}
Лицензировано под: CC-BY-SA с атрибуция
Не связан с StackOverflow
scroll top