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

我基本上是在寻找一个C ++版本的fdopen()。 我在这方面做了一些研究,看起来应该很容易,但其实是一件很复杂的事情。 我是否错过了这个信念(即它真的很容易)? 如果不是,那里有一个好的图书馆来处理这个问题吗?

编辑:将我的示例解决scheme移到一个单独的答案。

根据ÉricMalenfant的回答:

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

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


的libstdc ++

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

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

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

我们创build它传递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的构造函数采用POSIX文件描述符,但是缺less当前文档和代码。 还有另外一个非标准版本的ifstream的构造函数将FILE *

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

并没有logging(我什至不能find任何旧的文档,它会出现)。 我们将其称为(第1行),参数是调用_fdopen从POSIX文件句柄获取CstreamFILE *的结果。

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

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

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

你的编译器提供了一个基于FILE的fstream构造函数是很好的机会,即使它是非标准的。 例如:

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

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

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

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

我已经尝试了Piotr Dobrogost提出的针对libstdc ++的解决scheme,并发现它存在一个痛苦的缺陷:由于istream缺less适当的移动构造函数,因此很难将新构build的istream对象从创build函数。 它的另一个问题是它泄漏一个FILE对象(甚至不认为底层的posix文件描述符)。 这是避免这些问题的另一种解决scheme:

 #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模式下正常工作。

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

也就是说,我看到有几个地方指的是mds-utils或boost来帮助弥补这个缺口。