如何从一个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来帮助弥补这个缺口。