Getting FILE * from std :: fstream

Is there a (cross-platform) way to get a C FILE * handle from a C ++ std :: fstream?

The reason I ask is because my C ++ library accepts Fstreams and in one specific function, I would like to use the C library that accepts the FILE * file.

+47
c ++ c file file-io fstream
Sep 20 '08 at 21:19
source share
8 answers

The short answer is no.

The reason is that std::fstream not required to use FILE* as part of its implementation. Therefore, even if you manage to extract the file descriptor from the std::fstream object and manually create the FILE object, then you will have other problems, because now you will have two buffered objects writing the same file descriptor.

The real question is, why do you want to convert the std::fstream object to FILE* ?

Although I do not recommend it, you can try looking at funopen() .
Unfortunately, this is not a POSIX API (this is a BSD extension), so its portability is in question. This is probably why I can not find anyone who wrapped std::stream with such an object.

 FILE *funopen( const void *cookie, int (*readfn )(void *, char *, int), int (*writefn)(void *, const char *, int), fpos_t (*seekfn) (void *, fpos_t, int), int (*closefn)(void *) ); 

This allows you to create a FILE object and specify some functions that will be used to do the actual work. If you write the appropriate functions, you can get them to read from the std::fstream object on which the file is actually open.

+31
Sep 20 '08 at 21:43
source share

There is no standardized way. I assume this is because the C ++ standardization group did not want to assume that the file descriptor could be represented as fd.

Most platforms seem to provide a non-standard way to do this.

http://www.ginac.de/~kreckel/fileno/ provides a good record of the situation and provides code that hides all the platform rudeness, at least for GCC. Given how gross it is only on the GCC, I think I avoid doing it together if possible.

+16
Sep 20 '08 at 21:53
source share

UPDATE: see @Jettatura, what I consider the best answer is https://stackoverflow.com/a/312412/123124/. (Linux only).

ORIGINAL:

(Maybe not a cross platform, but simple)

Hacking simplification http://www.ginac.de/~kreckel/fileno/ (dvorak answer) and viewing this gcc extension http://gcc.gnu.org/onlinedocs/gcc-4.6.2/libstdc++/api/a00069. html # a59f78806603c619eafcd4537c920f859 , I have this solution that works on GCC (at least 4.8) and clang (at least 3.3)

 #include<fstream> #include<ext/stdio_filebuf.h> typedef std::basic_ofstream<char>::__filebuf_type buffer_t; typedef __gnu_cxx::stdio_filebuf<char> io_buffer_t; FILE* cfile_impl(buffer_t* const fb){ return (static_cast<io_buffer_t* const>(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());} 

and you can use it,

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

Limitations: (comments are welcome)

  • I believe that after fprintf printing before std::ofstream is important fflush , otherwise "sample2" will appear before "sample1" in the above example. I don't know if there is a better solution for this than using fflush . In particular, ofs << flush does not help.

  • It is not possible to extract the * file from std::stringstream , I don’t even know if this is possible. (see below for an update).

  • I still don't know how to extract C stderr from std::cerr , etc., for example, for use in fprintf(stderr, "sample") , in hypothetical code like this fprintf(cfile(std::cerr), "sample") .

Regarding the last limitation, the only workaround I found was to add these overloads:

 FILE* cfile(std::ostream const& os){ if(std::ofstream const* ofsP = dynamic_cast<std::ofstream const*>(&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<std::ostringstream const*>(&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<std::ifstream const*>(&is)) return cfile(*ifsP); if(&is == &std::cin) return stdin; if(dynamic_cast<std::ostringstream const*>(&is) != 0){ throw std::runtime_error("don't know how to extract FILE pointer from std::istringstream"); } return 0; // stream not recognized } 

Attempted to contact iostringstream

With fmemopen you can read using fscanf from istream , but this requires a lot of accounting and updating the input position of the stream after each read, if you want to combine C-reads and C ++ - reads. I could not convert this to a cfile function as shown above. (Maybe the cfile class, which keeps updating after every read, is the way to go).

 // hack to access the protected member of istreambuf that know the current position char* access_gptr(std::basic_streambuf<char, std::char_traits<char>>& bs){ struct access_class : std::basic_streambuf<char, std::char_traits<char>>{ 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; } 
+7
Nov 03 '13 at 1:48
source share

Well, you can get the file descriptor - I forget if the method is fd () or getfd (). The implementations that I used provide such methods, but the language standard does not require them, I believe that the standard should not care if your platform uses fd for files.

From this you can use fdopen (fd, mode) to get FILE *.

However, I believe that the mechanisms required by the standard for synchronizing STDIN / cin, STDOUT / cout, and STDERR / cerr need not be visible to you. Therefore, if you use both fstream and FILE *, buffering may interfere with you.

Also, if either Fstream or FILE closes, they probably close the underlying fd, so you need to make sure you clear BOTH before closing EITHER.

+4
Sep 20 '08 at 21:26
source share

In a single-threaded POSIX application, you can easily get the fd number in a portable way:

 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 

This method breaks down in a multi-threaded application if this code runs with other threads, opening file descriptors.

+3
Nov 04 '14 at 23:03
source share

Another way to do it on Linux:

 #include <stdio.h> #include <cassert> template<class STREAM> 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 <STREAM*>(cookie); writer->write(buf, size); return size; } int static cookieClose(void* cookie) { return EOF; } }; // STDIOAdapter 

Usage, for example:

 #include <boost/iostreams/filtering_stream.hpp> #include <boost/iostreams/filter/bzip2.hpp> #include <boost/iostreams/device/file.hpp> 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<filtering_ostream>::yield(&out); assert(fp > 0); fputs("Was up, Man", fp); fflush (fp); fclose(fp); return 1; } 
+2
Nov 09 '15 at 15:52
source share

Please look at this library.

MDS Utilities

It solves the problem because it allows you to treat C FILE * as a C ++ stream. It uses Boost C ++ libraries. You must use Doxygen to view the documentation.

+1
Jun 03 '09 at 9:14
source share

There is a way to get the file descriptor from fstream , and then convert it to FILE* (via fdopen ). Personally, I don’t see the need for FILE* , but with the file descriptor you can do many interesting things, such as redirection ( dup2 ).

Decision:

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

The last line works for libstdc ++. If you use some other library, you need to rebuild it a bit.

This trick is dirty and will bring out all private and public fstream members. If you want to use it in your production code, I suggest you create separate .cpp and .h using the same int getFdFromFstream(std::basic_ios<char>& fstr); function int getFdFromFstream(std::basic_ios<char>& fstr); . The header file must not include the fstream file.

+1
Jun 15 '17 at 21:46 on
source share



All Articles