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

后端 未结 7 1986
情歌与酒
情歌与酒 2020-11-22 12:36

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 v

相关标签:
7条回答
  • 2020-11-22 12:41

    It actually is quite easy. Nicolai M. Josuttis has released fdstream in conjunction with his book The C++ Standard Library - A Tutorial and Reference. You can find the 184 line implementation here.

    0 讨论(0)
  • 2020-11-22 12:47

    Part of the original (unstated) motivation of this question is to have the ability to pass data either between programs or between two parts of a test program using a safely created temporary file, but tmpnam() throws a warning in gcc, so I wanted to use mkstemp() instead. Here is a test program that I wrote based on the answer given by Éric Malenfant but using mkstemp() instead of fdopen(); this works on my Ubuntu system with Boost libraries installed:

    #include <stdlib.h>
    #include <string.h>
    #include <assert.h>
    #include <string>
    #include <iostream>
    #include <boost/filesystem.hpp>
    #include <boost/iostreams/device/file_descriptor.hpp>
    #include <boost/iostreams/stream.hpp>
    
    using boost::iostreams::stream;
    using boost::iostreams::file_descriptor_sink;
    using boost::filesystem::path;
    using boost::filesystem::exists;
    using boost::filesystem::status;
    using boost::filesystem::remove;
    
    int main(int argc, const char *argv[]) {
      char tmpTemplate[13];
      strncpy(tmpTemplate, "/tmp/XXXXXX", 13);
      stream<file_descriptor_sink> tmp(mkstemp(tmpTemplate));
      assert(tmp.is_open());
      tmp << "Hello mkstemp!" << std::endl;
      tmp.close();
      path tmpPath(tmpTemplate);
      if (exists(status(tmpPath))) {
        std::cout << "Output is in " << tmpPath.file_string() << std::endl;
        std::string cmd("cat ");
        cmd += tmpPath.file_string();
        system(cmd.c_str());
        std::cout << "Removing " << tmpPath.file_string() << std::endl;
        remove(tmpPath);
      }
    }
    
    0 讨论(0)
  • 2020-11-22 12:48

    I've tried the solution proposed above for libstdc++ by Piotr Dobrogost, and found that it had a painful flaw: Due to the lack of a proper move constructor for istream, it's very difficult to get the newly constructed istream object out of the creating function. Another issue with it is that it leaks a FILE object (even thought not the underlying posix file descriptor). Here's an alternative solution that avoids these issues:

    #include <fstream>
    #include <string>
    #include <ext/stdio_filebuf.h>
    #include <type_traits>
    
    bool OpenFileForSequentialInput(ifstream& ifs, const string& fname)
    {
        ifs.open(fname.c_str(), ios::in);
        if (! ifs.is_open()) {
            return false;
        }
    
        using FilebufType = __gnu_cxx::stdio_filebuf<std::ifstream::char_type>;
        static_assert(  std::is_base_of<ifstream::__filebuf_type, FilebufType>::value &&
                        (sizeof(FilebufType) == sizeof(ifstream::__filebuf_type)),
                "The filebuf type appears to have extra data members, the cast might be unsafe");
    
        const int fd = static_cast<FilebufType*>(ifs.rdbuf())->fd();
        assert(fd >= 0);
        if (0 != posix_fadvise(fd, 0, 0, POSIX_FADV_SEQUENTIAL)) {
            ifs.close();
            return false;
        }
    
        return true;
    }
    

    The call to posix_fadvise() demonstrates a potential use. Also note that the example uses static_assert and using which are C++ 11, other than that it should build just fine in C++ 03 mode.

    0 讨论(0)
  • 2020-11-22 12:54

    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 (This is the case for libstdc++, IIRC) or a FILE* as an input.

    Another alternative would be to use a boost::iostreams::file_descriptor device, which you could wrap in a boost::iostreams::stream if you want to have an std::stream interface to it.

    0 讨论(0)
  • 2020-11-22 12:55

    There's a good chance your compiler offers a FILE-based fstream constructor, even though it's non-standard. For example:

    FILE* f = fdopen(my_fd, "a");
    std::fstream fstr(f);
    fstr << "Greetings\n";
    

    But as far as I know, there's no portable way to do this.

    0 讨论(0)
  • 2020-11-22 12:59

    My understanding is that there is no association with FILE pointers or file descriptors in the C++ iostream object model in order to keep code portable.

    That said, I saw several places refer to the mds-utils or boost to help bridge that gap.

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