Flamefire
Flamefire

Reputation: 5807

When is calling basic_filebuf::pbackfail allowed/defined to succeed

On implementing a basic_filebuf I stumbled over basic_filebuf::pbackfail and don't fully understand its definition.

From cplusplus.com

Moves the current input position on position back to point to the previous character and, if supported, makes c available as that next character to be read.

If the implementation does not support writing to putback positions, c shall either match the character at the putback position or be the end-of-file value (traits_type::eof()). Otherwise, the function fails. [...]

If the get pointer (gptr) is at the beginning of the character sequence before the call, the function may either fail or make additional putback positions available and succeed, depending on the library implementation.

Or from cppreference:

1) The caller is requesting that the get area is backed up by one character (pbackfail() is called with no arguments), in which case, this function re-reads the file starting one byte earlier and decrements basic_streambuf::gptr(), e.g. by calling gbump(-1).
2) The caller attempts to putback a different character from the one retrieved earlier (pbackfail() is called with the character that needs to be put back), in which case
a) First, checks if there is a putback position, and if there isn't, backs up the get area by re-reading the file starting one byte earlier.
a) Then checks what character is in the putback position. If the character held there is already equal to c, as determined by Traits::eq(to_char_type(c), gptr()[-1]), then simply decrements basic_streambuf::gptr().
b) Otherwise, if the buffer is allowed to modify its own get area, decrements basic_streambuf::gptr() and writes c to the location pointed to gptr() after adjustment.

So in essence both say that the input position is decremented (unless it is at the start of the file) and possibly a char is put back. So the following should succeed (assume prepared file according to comments, using std classes for comparison of behavior):

std::fstream f(..., in | out | binary);
f.get() // == 'a'
f.get() // == 'b'
f.sync(); // or f.seekg(0)
f.putback('b');
f.putback('a');
f.putback(); // may fail

However on libc++ the first putback fails already and checking the source code I found pbackfail to be guarded by if (__file_ && this->eback() < this->gptr()) aka "if there is an open file and there is space at the front of the current read buffer".

A flush/sync/seek clears the read buffer which explains the failing putback. When using unbuffered IO there will only be a single char space in the read buffer so (at least) the 2nd putback will fail even without the flush. Or the second get might cross a buffer "border" which means "b" will be the first char in the current buffer which also makes the second putback fail.

Question: How is putback exactly specified? It seems to be only valid immediately after a get although both cppreference and cplusplus seem to imply that the read position is decremented in any case. If they are right, is libc++ non-conforming or am I missing anything?

Upvotes: 0

Views: 92

Answers (1)

Marshall Clow
Marshall Clow

Reputation: 16670

Question: How is putback exactly specified?

It is specified in the section istream.unformatted.

However, that refers to sputbackc, and you'll probably want to read the general stream buffer requirements as well.

Upvotes: 0

Related Questions