Получение FILE * из std :: fstream

Есть (кросс-платформенный) способ получить C FILE * дескриптор из C ++ std :: fstream?

Причина, по которой я спрашиваю, заключается в том, что моя библиотека на C ++ принимает fstream и в одной конкретной функции, я хотел бы использовать библиотеку C, которая принимает FILE *.

Нет стандартизированного способа. Я предполагаю, что это связано с тем, что группа стандартизации C ++ не хотела предполагать, что дескриптор файла может быть представлен как fd.

Большинство платформ, похоже, предоставляют некоторые нестандартные способы сделать это.

http://www.ginac.de/~kreckel/fileno/ обеспечивает хорошую запись ситуации и предоставляет код, который скрывает всю грубость платформы, по крайней мере для GCC. Учитывая, насколько важен это только на GCC, я думаю, что я избег бы сделать это вместе, если это возможно.

ОБНОВЛЕНИЕ: см. @Jettatura, что я думаю, что это лучший ответ https://stackoverflow.com/a/33612982/225186 (только Linux?).

ОРИГИНАЛ:

(Возможно, не перекрестная платформа, но простая)

Упрощение взлома в http://www.ginac.de/~kreckel/fileno/ (ответ dvorak) и просмотр этого расширения gcc http://gcc.gnu.org/onlinedocs/gcc-4.6.2/libstdc++/ api / a00069.html # a59f78806603c619eafcd4537c920f859 , у меня есть это решение, которое работает на GCC (по крайней мере, 4.8) и clang (по крайней мере 3.3)

 #include #include typedef std::basic_ofstream::__filebuf_type buffer_t; typedef __gnu_cxx::stdio_filebuf io_buffer_t; FILE* cfile_impl(buffer_t* const fb){ return (static_cast(fb))->file(); //type std::__c_file } FILE* cfile(std::ofstream const& ofs){return cfile_impl(ofs.rdbuf());} FILE* cfile(std::ifstream const& ifs){return cfile_impl(ifs.rdbuf());} 

и его можно использовать,

 int main(){ std::ofstream ofs("file.txt"); fprintf(cfile(ofs), "sample1"); fflush(cfile(ofs)); // ofs << std::flush; doesn't help ofs << "sample2\n"; } 

Ограничения: (комментарии приветствуются)

  1. Я считаю, что важно fflush после печати fprintf в std::ofstream , иначе «sample2» появляется перед «sample1» в примере выше. Я не знаю, есть ли лучшее решение для этого, чем использование fflush . Примечательно, что ofs << flush не помогает.

  2. Невозможно извлечь FILE * из std::stringstream , я даже не знаю, возможно ли это. (см. ниже для обновления).

  3. Я до сих пор не знаю, как извлечь stderr из std::cerr и т. Д., Например, для использования в fprintf(stderr, "sample") в гипотетическом коде, таком как fprintf(cfile(std::cerr), "sample") .

Что касается последнего ограничения, единственным обходным решением, которое я нашел, является добавление этих перегрузок:

 FILE* cfile(std::ostream const& os){ if(std::ofstream const* ofsP = dynamic_cast(&os)) return cfile(*ofsP); if(&os == &std::cerr) return stderr; if(&os == &std::cout) return stdout; if(&os == &std::clog) return stderr; if(dynamic_cast(&os) != 0){ throw std::runtime_error("don't know cannot extract FILE pointer from std::ostringstream"); } return 0; // stream not recognized } FILE* cfile(std::istream const& is){ if(std::ifstream const* ifsP = dynamic_cast(&is)) return cfile(*ifsP); if(&is == &std::cin) return stdin; if(dynamic_cast(&is) != 0){ throw std::runtime_error("don't know how to extract FILE pointer from std::istringstream"); } return 0; // stream not recognized } 

Попытка обработки iostringstream

Можно читать fscanf из fscanf с помощью fmemopen , но для этого требуется много учета и обновления входной позиции streamа после каждого чтения, если вы хотите объединить С-чтения и чтения на C ++. Я не смог преобразовать это в функцию cfile как cfile выше. (Возможно, class cfile который продолжает обновлять после каждого чтения, - это путь).

 // hack to access the protected member of istreambuf that know the current position char* access_gptr(std::basic_streambuf>& bs){ struct access_class : std::basic_streambuf>{ char* access_gptr() const{return this->gptr();} }; return ((access_class*)(&bs))->access_gptr(); } int main(){ std::istringstream iss("11 22 33"); // read the C++ way int j1; iss >> j1; std::cout << j1 << std::endl; // read the C way float j2; char* buf = access_gptr(*iss.rdbuf()); // get current position size_t buf_size = iss.rdbuf()->in_avail(); // get remaining characters FILE* file = fmemopen(buf, buf_size, "r"); // open buffer memory as FILE* fscanf(file, "%f", &j2); // finally! iss.rdbuf()->pubseekoff(ftell(file), iss.cur, iss.in); // update input stream position from current FILE position. std::cout << "j2 = " << j2 << std::endl; // read again the C++ way int j3; iss >> j3; std::cout << "j3 = " << j3 << std::endl; } 

Ну, вы можете получить дескриптор файла – я забыл, является ли метод fd () или getfd (). Реализации, которые я использовал, предоставляют такие методы, но стандарт языка не требует их, я считаю, стандарту не должно волновать, использует ли ваша платформа fd для файлов.

Из этого вы можете использовать fdopen (fd, mode), чтобы получить FILE *.

Тем не менее, я думаю, что механизмы, требуемые стандартом для синхронизации STDIN / cin, STDOUT / cout и STDERR / cerr, не должны быть вам видны. Поэтому, если вы используете как fstream, так и FILE *, буферизация может помешать вам.

Кроме того, если либо фид ИЛИ ФАЙЛ закрывается, они, вероятно, закрывают базовый fd, поэтому вам нужно убедиться, что вы запустили BOTH перед закрытием EITHER.

В однопоточном приложении POSIX вы можете легко получить номер fd переносимым образом:

 int fd = dup(0); close(fd); // POSIX requires the next opened file descriptor to be fd. std::fstream file(...); // now fd has been opened again and is owned by file 

Этот метод ломается в многопоточном приложении, если этот код запускается с другими streamами, открывая дескрипторы файлов.

еще один способ сделать это в Linux:

 #include  #include  template struct STDIOAdapter { static FILE* yield(STREAM* stream) { assert(stream != NULL); static cookie_io_functions_t Cookies = { .read = NULL, .write = cookieWrite, .seek = NULL, .close = cookieClose }; return fopencookie(stream, "w", Cookies); } ssize_t static cookieWrite(void* cookie, const char* buf, size_t size) { if(cookie == NULL) return -1; STREAM* writer = static_cast (cookie); writer->write(buf, size); return size; } int static cookieClose(void* cookie) { return EOF; } }; // STDIOAdapter 

Использование, например:

 #include  #include  #include  using namespace boost::iostreams; int main() { filtering_ostream out; out.push(boost::iostreams::bzip2_compressor()); out.push(file_sink("my_file.txt")); FILE* fp = STDIOAdapter::yield(&out); assert(fp > 0); fputs("Was up, Man", fp); fflush (fp); fclose(fp); return 1; } 

Пожалуйста, посмотрите на эту библиотеку

MDS utils

Он решает проблему, потому что позволяет обрабатывать C FILE * как stream C ++. Он использует библиотеки Boost C ++. Вы должны использовать Doxygen для просмотра документации.

Существует способ получить файловый дескриптор из fstream а затем преобразовать его в FILE* (через fdopen ). Лично я не вижу необходимости в FILE* , но с файловым дескриптором вы можете делать много интересного, например redirect ( dup2 ).

Решение:

 #define private public #define protected public #include  #undef private #undef protected std::ifstream file("some file"); auto fno = file._M_filebuf._M_file.fd(); 

Последняя строка работает для libstdc ++. Если вы используете какую-то другую библиотеку, вам нужно немного ее перестроить.

Этот трюк грязный и выведет всех частных и публичных членов fstream. Если вы хотите использовать его в своем производственном коде, я предлагаю вам создать отдельные .cpp и .h с помощью одной функции int getFdFromFstream(std::basic_ios& fstr); , Файл заголовка не должен включать fstream.

  • Чтение из текстового файла до тех пор, пока EOF не повторит последнюю строку
  • Получение файлового дескриптора из std :: fstream
  • C ++ Обработка файлов: разница между ios: app и ios: ate?
  • Давайте будем гением компьютера.