Я в основном ищу версию fdopen () на C ++. Я провел небольшое исследование по этому поводу, и это одна из тех вещей, которые кажутся простыми, но на деле оказываются очень сложными. Я что-то упустил в этой вере (т.е. это действительно легко)? Если нет, есть ли где-нибудь хорошая библиотека, чтобы справиться с этим?
РЕДАКТИРОВАТЬ: мой пример решения перенесен в отдельный ответ.
c++
posix
fstream
file-descriptor
BD в Ривенхилле
источник
источник
mmap
с файлом и отображать его содержимое как байтовый массив.Ответы:
Из ответа Эрика Маленфана:
На основании вышеизложенных наблюдений и моих исследований ниже рабочий код представлен в двух вариантах; один для 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), а затем передаем его конструктору istream как 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, но она отсутствует как в текущей документации, так и в коде. Есть еще одна нестандартная версия конструктора ifstream, принимающая FILE *
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; }
источник
std::cout
Хорошая идея - изучить реализацию. Мне интересно, в чем разница междуstdio_filebuf
иstdio_sync_filebuf
?AFAIK, в стандартном С ++ нет возможности сделать это. В зависимости от вашей платформы ваша реализация стандартной библиотеки может предлагать (как нестандартное расширение) конструктор fstream, принимающий файловый дескриптор (это случай для libstdc ++, IIRC) или a
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 (). Вот тестовая программа, которую я написал на основе ответа Эрика Маленфанта, но с использованием 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); } }
источник
На самом деле это довольно просто. Николай М. Йосуттис выпустил
fdstream
вместе со своей книгой Стандартная библиотека C ++ - Учебное пособие и справочник . Вы можете найти реализацию на 184 линии здесь .источник
Я попробовал решение, предложенное выше для 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.
источник
Насколько я понимаю, в объектной модели iostream C ++ нет никакой связи с указателями FILE или файловыми дескрипторами, чтобы код оставался переносимым.
Тем не менее, я видел несколько мест, где упоминаются mds-utils или boost, чтобы помочь преодолеть этот пробел.
источник