如何从POSIX文件描述符构造c ++ fstream?

时间:2010-04-30 16:43:37

标签: c++ posix fstream file-descriptor

我基本上是在寻找fdopen()的C ++版本。我对此做了一些研究,这似乎应该是容易的事情之一,但事实证明是非常复杂的。我是否缺少这种信念(即它真的很容易)?如果没有,是否有一个好的图书馆可以处理这个?

编辑:将我的示例解决方案移到单独的答案中。

7 个答案:

答案 0 :(得分:66)

ÉricMalenfant给出的答案:

  

AFAIK,没有办法做到这一点   标准C ++。取决于你的   平台,你的实施   标准库可能提供(作为   非标准扩展)fstream   构造函数采用文件描述符   作为输入。 (这是的情况   libstdc ++,IIRC)或文件*。

基于以上观察和我的研究,下面有两种变体的工作代码;一个用于libstdc ++,另一个用于Microsoft Visual C ++。


的libstdc ++

有非标准__gnu_cxx::stdio_filebuf类模板,它继承std::basic_streambuf并具有以下构造函数

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

with description 此构造函数将文件流缓冲区与打开的POSIX文件描述符相关联。

我们通过POSIX句柄(第1行)创建它,然后我们将它作为basic_streambuf(第2行)传递给istream的构造函数:

#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 ++

曾经有过ifstream的构造函数的非标准version采用POSIX文件描述符,但它从current文档和代码中都缺失了。还有另一个非标准版本的ifstream构造函数采用FILE *

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

并没有记录(我甚至找不到任何旧的文档,它会出现在哪里)。我们称之为(第1行),参数是调用_fdopen以从POSIX文件句柄获取C流FILE *的结果。

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

答案 1 :(得分:36)

AFAIK,在标准C ++中无法做到这一点。根据您的平台,您的标准库实现可能会提供(作为非标准扩展)fstream构造函数,它采用文件描述符(这是libstdc ++,IIRC的情况)或FILE*作为输入。

另一个替代方法是使用boost::iostreams::file_descriptor设备,如果你想拥有一个std :: stream接口,你可以将其包装在boost::iostreams::stream中。

答案 2 :(得分:8)

您的编译器很有可能提供基于FILE的fstream构造函数,即使它是非标准的。例如:

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

但据我所知,没有可行的方法可以做到这一点。

答案 3 :(得分:8)

这个问题的原始(未说明)动机的一部分是能够使用安全创建的临时文件在程序之间或测试程序的两个部分之间传递数据,但是tmpnam()在gcc中抛出警告,所以我想用mkstemp()代替。这是我根据ÉricMalenfant给出的答案编写的测试程序,但是使用mkstemp()而不是fdopen();这适用于安装了Boost库的我的Ubuntu系统:

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

答案 4 :(得分:4)

我已经尝试过Piotr Dobrogost上面针对libstdc ++提出的解决方案,并发现它有一个痛苦的缺陷:由于缺乏适合istream的移动构造函数,因此很难获得新构造的istream对象来自创建函数。它的另一个问题是它泄漏了一个FILE对象(甚至认为不是底层的posix文件描述符)。这是避免这些问题的替代解决方案:

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

对posix_fadvise()的调用表明了潜在的用途。另请注意,该示例使用 static_assert 使用这些是C ++ 11,除此之外它应该在C ++ 03模式下正常构建。

答案 5 :(得分:3)

实际上很容易。 Nicolai M. Josuttis与他的书The C++ Standard Library - A Tutorial and Reference一起发布了fdstream。您可以执行184行here

答案 6 :(得分:-4)

我的理解是,在C ++ iostream对象模型中没有与FILE指针或文件描述符的关联,以保持代码的可移植性。

那就是说,我看到有几个地方提到了mds-utils或者提升来帮助缩小差距。