Как построить C ++ FSTReam из файлового дескриптора POSIX?
-
02-10-2019 - |
Вопрос
Я в основном ищу версию C ++ FDOPEN (). Я сделал немного исследований по этому поводу, и это одна из тех вещей, которые похоже, это должно быть легко, но оказывается очень сложным. Я упучаю что-то в этом убеждении (то есть это действительно легко)? Если нет, есть ли хорошая библиотека там где-то справиться с этим?
Редактировать: Переместил мой пример решения для отдельного ответа.
Решение
Из ответа, данного Eric Malenfant:
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))
с описанием Этот конструктор объединяет буфер потока файлов с помощью дескриптора файла POSIX.
Мы создаем его прохождение ручки POSIX (строку 1), а затем мы передаем его для конструктора Isstream как Basic_streambuf (строка 2):
#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, но он отсутствует как из Текущий Документы и от кода. Есть еще одна нестандартная версия конструктора IFTREAM, принимающая файл *
explicit basic_ifstream(_Filet *_File)
: _Mybase(&_Filebuffer),
_Filebuffer(_File)
{ // construct with specified C stream
}
И это не задокументировано (я даже не мог найти какую-либо старую документацию, где она будет присутствовать). Мы называем это (строка 1) с параметром, являющимся результатом вызова _fdopen. Чтобы получить файл потока C * из файла POSIX файла.
#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*
в качестве ввода.
Другая альтернатива будет использовать Boost :: IoStreams :: file_descriptor устройство, которое вы можете обернуть в Boost :: IoStreams :: Stream Если вы хотите иметь интерфейс STD :: Stream к нему.
Существует хороший шанс, что ваш компилятор предлагает файловой конструктор FStream, хотя он нестандартный. Например:
FILE* f = fdopen(my_fd, "a");
std::fstream fstr(f);
fstr << "Greetings\n";
Но насколько я знаю, нет портативного способа сделать это.
Часть оригинальной (ненужденной) мотивации этого вопроса состоит в том, чтобы иметь возможность передавать данные между программами, либо между двумя частями программы тестирования, используя безопасно созданный временный файл, но TMPNAM () бросает предупреждение в GCC, поэтому я хотел Использовать mkstemp () вместо этого. Вот программа тестирования, которую я написал на основе ответа, данного эриком Malenfant, но с помощью 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);
}
}
Я попробовал решение, предложенное выше для LibStdc ++ Piotr Dobrogost, и обнаружил, что у него было болезненный недостаток: из-за отсутствия правильного конструктора движения для ISTream, очень трудно получить вновь построенный объект ISTREAM из создания функции Отказ Другая проблема с этим, заключается в том, что она утеряет объект файла (даже подумал не базовый файл 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.
Это на самом деле довольно легко. Николай М. Джозуттис выпустил fdstream
в сочетании со своей книгой Стандартная библиотека C ++ - учебное пособие и ссылка. Отказ Вы можете найти 184 строки реализации здесь.
Мое понимание состоит в том, что нет связи с указателями файлов или дескрипторами файлов в модели объекта C ++ IOSTream, чтобы сохранить портативный код.
Что сказал, я видел несколько мест, относящихся к MDS-UTILS. или повысить, чтобы помочь мостику этого пробела.