Search code examples
c++posixfstreamfile-descriptor

How to construct a c++ fstream from a POSIX file descriptor?


I'm basically looking for a C++ version of fdopen(). I did a bit of research on this and it is one of those things that seems like it should be easy, but turns out to be very complicated. Am I missing something in this belief (i.e. it really is easy)? If not, is there a good library out there somewhere to handle this?

EDIT: Moved my example solution to a separate answer.


Solution

  • From the answer given by Éric Malenfant:

    AFAIK, there is no way to do this in standard C++. Depending on your platform, your implementation of the standard library may offer (as a nonstandard extension) a fstream constructor taking a file descriptor as input. (This is the case for libstdc++, IIRC) or a FILE*.

    Based on above observations and my research below there's working code in two variants; one for libstdc++ and another one for Microsoft Visual C++.


    libstdc++

    There's non-standard __gnu_cxx::stdio_filebuf class template which inherits std::basic_streambuf and has the following constructor

    stdio_filebuf (int __fd, std::ios_base::openmode __mode, size_t __size=static_cast< size_t >(BUFSIZ)) 
    

    with description This constructor associates a file stream buffer with an open POSIX file descriptor.

    We create it passing POSIX handle (line 1) and then we pass it to istream's constructor as basic_streambuf (line 2):

    #include <ext/stdio_filebuf.h>
    #include <iostream>
    #include <fstream>
    #include <string>
    
    using namespace std;
    
    int main()
    {
        ofstream ofs("test.txt");
        ofs << "Writing to a basic_ofstream object..." << endl;
        ofs.close();
    
        int posix_handle = fileno(::fopen("test.txt", "r"));
    
        __gnu_cxx::stdio_filebuf<char> filebuf(posix_handle, std::ios::in); // 1
        istream is(&filebuf); // 2
    
        string line;
        getline(is, line);
        cout << "line: " << line << std::endl;
        return 0;
    }
    

    Microsoft Visual C++

    There used to be non-standard version of ifstream's constructor taking POSIX file descriptor but it's missing both from current docs and from code. There is another non-standard version of ifstream's constructor taking FILE*

    explicit basic_ifstream(_Filet *_File)
        : _Mybase(&_Filebuffer),
            _Filebuffer(_File)
        {   // construct with specified C stream
        }
    

    and it's not documented (I couldn't even find any old documentation where it would be present). We call it (line 1) with the parameter being the result of calling _fdopen to get C stream FILE* from POSIX file handle.

    #include <cstdio>
    #include <iostream>
    #include <fstream>
    #include <string>
    
    using namespace std;
    
    int main()
    {
        ofstream ofs("test.txt");
        ofs << "Writing to a basic_ofstream object..." << endl;
        ofs.close();
    
        int posix_handle = ::_fileno(::fopen("test.txt", "r"));
    
        ifstream ifs(::_fdopen(posix_handle, "r")); // 1
    
        string line;
        getline(ifs, line);
        ifs.close();
        cout << "line: " << line << endl;
        return 0;
    }