Does stream::seekoff update the input sequence?
Asked Answered
A

1

10

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?

Adrell answered 25/7, 2018 at 8:0 Comment(0)
B
2

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.

Baptistery answered 6/8, 2018 at 16:26 Comment(14)
So if the last operation was input, why will seekoff for filebuf update the input sequence? The input sequence is a buffer, which is independent of the file position and it should be updated to sync with the file position. Is my understanding wrong?Adrell
@Adrell So the class template std::basic_filebuf holds a single file position. There is no independent tracking of input and output positions in this case, only a restriction pertaining to whether you are permitted to read or write or both. I'll edit the answer to better reflect this point and add some explanation in the context of your comment.Baptistery
fseek/fsetpos only changes the underlying file position, which does not affect the C++ stream buffer (input sequence), is this right?Adrell
@Adrell Yes you are correct. And by the spec itself some details are implementation dependent. For example see here: eel.is/c++draft/filebuf#virtuals-19. And this is from cppreference.com: "sync() or its equivalent is implicitly called by close(), seekoff(), and seekpos() and explicitly called by std::basic_streambuf::pubsync()". See here at the Notes section: en.cppreference.com/w/cpp/io/basic_filebuf/sync.Baptistery
Yes, if "sync() or its equivalent is implicitly called by close(), seekoff(), and seekpos()", everything is fine. But I cannot find any support in the standard for this assertion. This is what exactly I asked.Adrell
@Adrell Just to be clear, are you considering the possibility that for example istream:seekg on an fstream/ifstream might not sync the input sequence, even though it is the main thing for it to do? The standard says seekg is implemented in terms of seekoff: eel.is/c++draft/istream.unformatted#43Baptistery
Yes, I think it should be synced but I cannot find an evidence in the specification.Adrell
For the avid reader, here are two official issues, not directly related to the OP but just might shed some light: open-std.org/jtc1/sc22/wg21/docs/lwg-defects.html#171, open-std.org/jtc1/sc22/wg21/docs/lwg-defects.html#2473Baptistery
@Adrell There is no evidence in the specification even for guaranteeing the mere existence of stream buffers (for input/output sequences) in the implementation. See here for example, when trying to manually provide them: eel.is/c++draft/filebuf#virtuals-12. It says for setbuf: "... the results are implementation-defined." But again, it does say that "a joint file position is maintained ... [for both sequences]": eel.is/c++draft/filebuf#3.3. Is this not enough evidence to claim that an implementation must keep both sequences in sync with the file pointer?Baptistery
But why does seekpos explicitly say "update the input sequence" while seekoff does not? This seems inconsistent.Adrell
@Adrell Such inconsistency in the wording of the standard may well be the case, but I think you'll agree the OP wasn't directly about the inconsistency as much as it was trying to figure out whether updating of the input sequence is guaranteed or not.Baptistery
I don't think "a joint file position is maintained ... [for both sequences]" is an evidence. I think this sentence is saying the input sequence and the output sequence share the same file pointer, rather than synchronization. I mentioned that inconsistency since if the synchronization is guaranteed by "a joint file position is maintained ... [for both sequences]", then it is unreasonable to say "update the input sequence" again.Adrell
@Adrell So if the sentence is saying the sequences share the same file pointer, and you acknowledge that intermediate buffers aren't necessitated by the spec, then what is the meaning of not being synchronized under these assertions? As in, the lack of evidence for existence of stream buffers is the further evidence for the synchronization, and thus it follows that "update the input sequence" is a mere inconsistency.Baptistery
Let us continue this discussion in chat.Baptistery

© 2022 - 2024 — McMap. All rights reserved.