Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Does stream::seekoff update the input sequence?

In [filebuf.virtuals]:

pos_type seekoff(off_type off, ios_base::seekdir way,
                 ios_base::openmode which
                   = ios_base::in | ios_base::out) override;

Effects: Let width denote a_­codecvt.encoding(). If is_­open() == false, or off != 0 && width <= 0, then the positioning operation fails. Otherwise, if way != basic_­ios​::​cur or off != 0, and if the last operation was output, then update the output sequence and write any unshift sequence. Next, seek to the new position: if width > 0, call fseek(file, width * off, whence), otherwise call fseek(file, 0, whence).

It does not mention that this function updates the input sequence. As a contrast, seekpos does update the input sequence:

pos_type seekpos(pos_type sp,
                 ios_base::openmode which
                   = ios_base::in | ios_base::out) override;

Alters the file position, if possible, to correspond to the position stored in sp (as described below). Altering the file position performs as follows:

  1. if (om & ios_­base​::​out) != 0, then update the output sequence and write any unshift sequence;

  2. set the file position to sp as if by a call to fsetpos;

  3. if (om & ios_­base​::​in) != 0, then update the input sequence;

So is seekoff guaranteed to update the input sequence?

For a concrete example, consider:

#include <fstream>
#include <iostream>

int main()
{
    std::fstream f("test.txt"); // test.txt contains "test"
    char ch;
    f >> ch;
    f.rdbuf()->pubseekoff(0, std::ios_base::beg);
    f >> ch;
    std::cout << ch;
}

Is the program guaranteed to output t?

like image 937
xskxzr Avatar asked Jul 25 '18 08:07

xskxzr


1 Answers

I can see your point friend, and indeed this may be a source of some confusion not only for yourself.

Short answer:

Yes, seekoff will update the the input sequence just as seekpos will. Both seekoff and seekpos behave the same regarding which sequence is being updated by the call, input or output (or both).

Long explanation:

Not by way of convention alone, but according to the standard itself, the behavior of both seekoff and seekpos is defined to be dependent of the ios_base::openmode which argument. As can be seen in another class template, stringbuf, derived from the same parent as filebuf, the override for seekoff explicitly states that for (which & ios_­base​::​in) == ios_­base​::​in the call will position the input sequence; for (which & ios_­base​::​out) == ios_­base​::​out the call will position the output sequence; for (which & (ios_­base​::​in | ios_­base​::​out)) == (ios_­base​::​in | ios_­base​::​out) and way == either ios_­base​::​beg or ios_­base​::​end the call will position both the input and the output sequences.

But when working directly in front of the standard, one need not expect things to just present themselves. See here under the parent class streambuf:

pos_type seekoff(off_type off, ios_base::seekdir way,
                 ios_base::openmode which
                   = ios_base::in | ios_base::out) override;

Effects: Alters the stream positions within one or more of the controlled sequences in a way that is defined separately for each class derived from basic_­streambuf ...

So, by looking more carefully in the standard for the quote you have provided yourself regarding seekpos of filebuf:

pos_type seekpos(pos_type sp,
                 ios_base::openmode which
                   = ios_base::in | ios_base::out) override;

Alters the file position, if possible, to correspond to the position stored in sp (as described below). Altering the file position performs as follows:

  1. if (om & ios_­base​::​out) != 0, then update the output sequence and write any unshift sequence;

  2. set the file position to sp as if by a call to fsetpos;

  3. if (om & ios_­base​::​in) != 0, then update the input sequence;

the following line says:

where om is the open mode passed to the last call to open(). ...

So this means that you cannot specify in the call itself which sequence you want to update. As in, the standard here says that the implementation should plainly ignore (!) the om argument.

Another bit we need not miss is in the quote you have provided regarding seekoff, where it says:

Next, seek to the new position: if width > 0, call fseek(file, width * off, whence), otherwise call fseek(file, 0, whence).

So underlying it's just a call to fseek. But on which particular FILE object? Are there separate ones for input and output? I believe the answer we're looking for appears in spec under filebuf:

§ 27.9.1.1

  1. The class basic_filebuf associates both the input sequence and the output sequence with a file.
  2. The restrictions on reading and writing a sequence controlled by an object of class basic_filebuf are the same as for reading and writing with the Standard C library FILEs.
  3. In particular:
    • If the file is not open for reading the input sequence cannot be read.
    • If the file is not open for writing the output sequence cannot be written.
    • A joint file position is maintained for both the input sequence and the output sequence.

As in, both seekoff and seekpos behaves the same regarding which sequence is being updated by the call, input or output (or both), and it is determined only by what was passed to open().

Also, just encountered this from about 5 years ago I see: fstream seekg(), seekp(), and write()

Edit, for further clarification:

Note the spec for seekoff says:

if the last operation was output, then update the output sequence and write any unshift sequence.

seekpos also says:

update the output sequence and write any unshift sequence;

It is the remarks section for seekoff that defines what “Write any unshift sequence” means. And as such it should be equivalent for both methods. But then both specify further: seekoff says it calls fseek and seekpos says it calls fsetpos (identical to fseek in this regard).

The reason for this, and for even mentioning the last operation was output bit, is found when considering that point 2 from section § 27.9.1.1 brought above is explained here in the C11 standard, ISO/IEC 9899:2011:

§7.21.5.3 The fopen function

¶7 When a file is opened with update mode ('+' as the second or third character in the above list of mode argument values), both input and output may be performed on the associated stream. However, output shall not be directly followed by input without an intervening call to the fflush function or to a file positioning function (fseek, fsetpos, or rewind), and input shall not be directly followed by output without an intervening call to a file positioning function, unless the input operation encounters end-of-file.

So to answer your comment below, whether seekoff will update the input sequence is regardless of whether the last operation was input. If the last operation wasn't input, then there's the technicality with the unshift sequence discussed above. But part of the idea for the whole stream classes is to encapsulate i/o in a way that doesn't bother you with such maintenance chores.

like image 161
Geezer Avatar answered Oct 28 '22 05:10

Geezer