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

声明:本页面是StackOverFlow热门问题的中英对照翻译,遵循CC BY-SA 4.0协议,如果您需要使用它,必须同样遵循CC BY-SA许可,注明原文地址和作者信息,同时你必须将它归于原作者(不是我):StackOverFlow 原文地址: http://stackoverflow.com/questions/2746168/
Warning: these are provided under cc-by-sa 4.0 license. You are free to use/share it, But you must attribute it to the original authors (not me): StackOverFlow

提示:将鼠标放在中文语句上可以显示对应的英文。显示中英文
时间:2020-08-28 00:34:44  来源:igfitidea点击:

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

c++posixfstreamfile-descriptor

提问by BD at Rivenhill

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?

我基本上是在寻找 fdopen() 的 C++ 版本。我对此进行了一些研究,这是看起来应该很容易的事情之一,但结果却非常复杂。我是否在这个信念中遗漏了什么(即它真的很容易)?如果没有,是否有一个很好的图书馆可以处理这个问题?

EDIT: Moved my example solution to a separate answer.

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

采纳答案by Piotr Dobrogost

From the answer given by éric Malenfant:

来自 é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*.

AFAIK,在标准 C++ 中无法做到这一点。根据您的平台,您的标准库实现可能会提供(作为非标准扩展)一个 fstream 构造函数,将文件描述符作为输入。(这是 libstdc++、IIRC 的情况)或 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++,另一个用于 Microsoft Visual C++。



libstdc++

库标准++

There's non-standard __gnu_cxx::stdio_filebufclass template which inherits std::basic_streambufand has the following constructor

有非标准的__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 This constructor associates a file stream buffer with an open POSIX file descriptor.

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

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

我们通过 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++

微软 Visual C++

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

曾经有采用POSIX 文件描述符的 ifstream 构造函数的非标准版本,但它在当前文档和代码中都没有。还有另一个非标准版本的 ifstream 构造函数采用 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 _fdopento get C stream FILE* from POSIX file handle.

并且它没有记录(我什至找不到任何旧文档存在的地方)。我们调用它(第 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;
}

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

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

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

另一种选择是使用boost::iostreams::file_descriptor设备,如果您想要一个 std::stream 接口,您可以将其包装在boost::iostreams::stream 中

回答by Darryl

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

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

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.

但据我所知,没有可移植的方法来做到这一点。

回答by BD at Rivenhill

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:

这个问题的原始(未说明)动机的一部分是能够使用安全创建的临时文件在程序之间或测试程序的两个部分之间传递数据,但是 tmpnam() 在 gcc 中抛出警告,所以我想要使用 mkstemp() 代替。这是我根据 éric Malenfant 给出的答案编写的测试程序,但使用 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);
  }
}

回答by YitzikC

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:

我已经尝试了上面 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;
}

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

对 posix_fadvise() 的调用展示了一种潜在用途。另请注意,该示例使用了static_assert使用了C++ 11,除此之外,它应该在 C++ 03 模式下构建得很好。

回答by Mark

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

这实际上很容易。Nicolai M. Josuttisfdstream与他的书The C++ Standard Library - A Tutorial and Reference 一起发布。您可以在此处找到 184 行实现。

回答by plinth

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.

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

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

也就是说,我看到有几个地方提到了mds-utils或 boost 来帮助弥合这一差距。