3

How can I get a C++ streambuf object from a C FILE* so that it uses the FILE object’s buffer, avoiding both objects to manage separate buffers while pointing to the same underlying file.

It looks like there is no standard way of doing that.

Boost has a stream_buffer class that has a constructor taking a file descriptor, allowing the use C++features on a file opened with C code. For example:

boost::iostreams::stream_buffer<boost::iostreams::file_descriptor_sink> mysb (fileno(myFileptr), boost::iostreams::never_close_handle));

myFileptr being of type FILE*. The problem is that mysb and the FILE object pointed at by myFileptr will maintain separate buffers, so this is not an option…

Here is the context of my problem. I have to provide a dynamically linkable library with a C interface (all exported variables, function arguments and function return values must have C types), but the library is actually developed in C++.

If I export a function, internally using C++ stream facilities, with this prototype

Void MyExportedFunct( FILE* myfile)

And, I import the library in a C program:

int main()
{
    FILE * fptr = fopen(“SomeFile”, "w");
    fprintf(fptr, “Some data.”)
    MyExportedFunct(fptr);
    fprintf(fptr, “Some more data…”)
    return 0; 
} 

I would like the data to be written to the file actually sent in the correct order (avoiding interleaved characters).

Any suggestion on how to achieve this, or for another approach, is welcome.

Guett31
  • 258
  • 2
  • 15
  • Smart quotes like `“SomeFile”` show this isn't the actual code. Sorry about removing and restoring the C tag. – Weather Vane Jan 25 '17 at 19:03
  • @WeatherVane I'm not sure you were wrong. This is definitely a question about C++, the fact that it uses facilities that were inherited from C shouldn't be relevant. – Mark Ransom Jan 25 '17 at 19:07
  • @MarkRansom I was put down recently for removing a C tag when there was a mention of C in the question. Here, OP is importing the library in a C program. – Weather Vane Jan 25 '17 at 19:11
  • Maybe you want [`ios_base::sync_with_stdio()`](http://www.cplusplus.com/reference/ios/ios_base/sync_with_stdio/)? – G. Sliepen Jan 25 '17 at 21:07
  • Yes, questions about interoperability between C and C++ code (which this is) should have both tags – Ben Voigt Jan 25 '17 at 21:40
  • @G.Sliepen: `sync_with_stdio` only works on `stdin`, `stdout`, `stderr` and not arbitrary file handles. – Ben Voigt Jan 25 '17 at 21:55

1 Answers1

3

There exists a (non-standard, looking for the reference now) C++ streambuf that is a layer on top of stdio, then it would be sufficient to constantly flush the stream buffer.

The automatic-flush part is portably accomplished using std::unitbuf:

mysb << std::unitbuf;

Ahh, it's named stdio_filebuf and comes with gcc's C++ library: __gnu_cxx::stdio_filebuf::stdio_filebuf constructor

Ben Voigt
  • 277,958
  • 43
  • 419
  • 720
  • As @ben-voigt points out, this is a gcc extension. There's no portable way to do this. – Marshall Clow Jan 25 '17 at 22:21
  • 3
    @MarshallClow: Which is unfortunate, because the standard library is absolutely required to make `streambuf` classes that layer on top of `FILE*` to get the correct `sync_with_stdio()` behavior for `cin`, `cout`, `cerr`, and `clog`. Those classes just aren't exposed in any standard way for use with other files/streams. – Ben Voigt Jan 25 '17 at 22:28
  • @BenVoigt: If the C `FILE` object and `stdio_filebuf` object share the same underlying buffer, there no need to constantly flush it. Right? – Guett31 Jan 26 '17 at 00:05
  • 1
    @Guett31: You assume there's only one buffer, while in fact they are layered. `stdio_filebuf` uses buffered writes (such as `fwrite`) to access the `FILE*`; those pass through the stdio buffer. But that doesn't mean that there is no buffering in `stdio_filebuf` itself, in order to pass larger chunks to `fwrite` at a time. In fact input `streambuf` objects need a buffer in order to implement `ungetc`. – Ben Voigt Jan 26 '17 at 00:12
  • @BenVoigt It makes sense. Thank you. The constructor of `stdio_filebuf` that you mentioned in your answer takes an argument for the buffer size, and I was thinking about setting it to 0. However, the fact that it is referred as the `preferred size of internal buffer` combined with your explanation about `ungetc` makes me think that even if I pass it 0, it might not actually be 0. – Guett31 Jan 26 '17 at 01:33