Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Why we flush a stream but not a buffer?

Tags:

c++

c

buffer

fflush

I know this is gonna be quite a stupid question, but after reading so many document about the whole "buffer" system, I can't understand why would people flush a stream but not a buffer.

I have seen people write thing like this:

FILE* file=fopen("mytext.txt","wr");
char buffer[10]="";
setbuf(file,buffer);

//do some stuff....

fflush(file);
....
fclose(file);

So I wonder, since we actually store things in the buffer, why do we flush the stream it is associated to rather than flush the buffer directly, which actually store something and should be flushed.( well, some people tell me that if things go like what I said it will just be the same thing so bother myself......)

For example, we can't write things likefflush(buffer).Why?

like image 805
walkerlala Avatar asked May 29 '15 22:05

walkerlala


2 Answers

Flushing copies data from the stream's internal buffer to the underlying file.

So the flushing function needs to know the source and the destination to copy.

This depends on I/O implementation, for C++ <iostream> see Jerry Coffin's answer - the buffers in <iostream> are more smart.

With C-style <cstdio>, if you want to flush with just one argument, either FILE* or the char array needs to know about the file it should copy to.

Your buffer is a dumb array, it just stores data for reading/writing. Since there are no additional info there, the function that gets a pointer to the buffer can't know the destination where to write it - so the imaginary fflush call would look like fflush(buffer, file); which doesn't get you anywhere. On the other hand, the FILE* stores the pointer to your buffer (you set the pointer with the setbuf(file,buffer) function call).

like image 199
milleniumbug Avatar answered Oct 01 '22 18:10

milleniumbug


The following deals only with iostreams and their buffer objects. For information about the buffers related to C-style I/O, please see @milleniumbug's answer.

Mostly because you (at least normally) want the stream's badbit set if attempting to flush the underlying buffer fails.

There's also a slightly complex little dance that streams use when interacting with the underlying stream, where the stream creates a sentry object, then carries out an action, then the sentry object is destroyed. The sentry is intended to make prefix and suffix operations exception safe.

So the overall sequence is something like this:

create sentry
if that succeeds (sentry converts to true) call rdbuf()->pubsync()
    if that fails (returns -1) setstate(badbit);

A stream buffer (e.g., a basic_filebuf) is directly attached to the underlying file system object--in fact, all the interaction between the iostream object and the underlying file object is done via the file buffer. As shown above, when the stream object does need to flush the buffer, all it needs to do it tell the buffer to flush itself by calling the buffer's pubsync() member function.

[For reference: [ostream.unformatted]/7:

basic_ostream& flush();

Effects: Behaves as an unformatted output function (as described in 27.7.3.6.1, paragraph 1). If rdbuf() is not a null pointer, constructs a sentry object. If this object returns true when converted to a value of type bool the function calls rdbuf()->pubsync(). If that function returns -1 calls setstate(badbit) (which may throw ios_base::failure (27.5.5.4)). Otherwise, if the sentry object returns false, does nothing.

Returns: *this.

...and [ofstream.cons]/2:

explicit basic_ofstream(const char* s, ios_base::openmode mode = ios_base::out);

Effects: Constructs an object of class basic_ofstream, initializing the base class with basic_ostream(&sb) and initializing sb with basic_filebuf()) (27.7.3.2, 27.9.1.2), then calls rdbuf()->open(s, mode|ios_base::out). If that function returns a null pointer, calls setstate(failbit).

like image 35
Jerry Coffin Avatar answered Oct 01 '22 19:10

Jerry Coffin