4

While writing some code to update a position in a binary file I noticed something strange. Consider this example code:

#include <iostream>
#include <fstream>
#include <string>
using namespace std;

int main() {
    char tmp;
    string s;
    fstream fs;

    fs.open("test.txt", fstream::out);
    fs << "blub" << endl;
    fs.close();
    fs.open("test.txt", fstream::in);
    fs >> s;
    cout << s << endl;
    fs.close();

    fs.open("test.txt", ios::in|ios::out|ios::binary);
    if (!fs.is_open() || !fs.good())
        cerr << "could not open!" << endl;

    fs.read(&tmp, 1);
    fs.read(&tmp, 1);
    //fs.tellg(); //<-- required to fix for old g++?
    const char *c = "ah";
    fs.write(&c[0], 1);
    fs.write(&c[1], 1);
    fs.close();

    fs.open("test.txt", fstream::in);
    fs >> s;
    cout << s << endl;
}

In recent g++ versions (at least with 6.2.1), I can just read and then write some bytes without problems - In the example you get the correct output:

blub
blah

Then I compiled the code with g++ 4.7.2 and suddenly the update has no effect, i.e. the second output is still "blub", unless I add fs.tellg() or fs.tellp(). I've found this question, but as I understand, this is a limitation under Windows, but I am working under Linux.

Now I wonder, is this a bug in the old g++ or am I doing it wrong and was just lucky with the modern g++ version, where it just works? Second question, why does asking for the current position fix it?

Thanks in advance!

Community
  • 1
  • 1
apirogov
  • 1,296
  • 1
  • 12
  • 22
  • The answer you linked says that what you're doing is not standard compliant, so yes you got lucky that old versions of gcc were not standard compliant, but now it appears it is. – AndyG Oct 06 '16 at 17:42
  • Probably you mean the other way round? Because it works in the recent g++ and is broken in the old one, but if it is not supposed to work, this would mean that the NEW version is LESS standard compliant? Which does not make sense to me. – apirogov Oct 06 '16 at 17:47
  • @apirgov: Sorry I mixed up the versions you were talking about. Yes, it sounds like the newer one has broken standards compliance. – AndyG Oct 06 '16 at 18:31
  • The standard hasn't changed on this topic in C++14 (n4140). § 27.9.1.1 /2 states "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." – AndyG Oct 06 '16 at 18:36
  • So I think this is effectively a dup of the answer you linked. There's a bug in gcc, and you should submit a bug report. – AndyG Oct 06 '16 at 18:37
  • @AndyG which gcc are you claiming to have a bug? – eerorika Oct 06 '16 at 19:09
  • @user2079303: Yeah it's a little hard to understand OP's post in concert with the link, but if you read closely, you'll see that it's the latest version of gcc that is not standards compliant (that is, with the latest, you don't need a stream manipulation call between reads and writes). Your answer is inaccurate in this regard. – AndyG Oct 06 '16 at 19:53
  • @AndyG The standard simply requires that the user code must do the intervening manipulation. In effect, this **allows** the implementation to require it, but it doesn't **require** the implementation to have the requirement. User code that violates the rule has undefined behaviour. The standard doesn't place any restrictions on the behaviour of the implementation in such case. Since there is no restriction on the behaviour of the implementation, the new gcc cannot be violating the standard in this case. – eerorika Oct 06 '16 at 20:00
  • @user2079303: That makes sense. Reading more on it seems to confirm what you've said. Definitely a requirement on the user code, not the implementation. – AndyG Oct 06 '16 at 20:14

1 Answers1

2

Now I wonder, is this a bug in the old g++

No, there is no bug in g++ in this regard.

or am I doing it wrong

Yes. This is explained in the answer that you linked.

... 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.

Which is from C standard but made relevant by:

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.

There is a bug, but it is in your code. Your program doesn't meet the requirements laid out by the standard.

but as I understand, this is a limitation under Windows

That may possibly be the case, but more generally the limitation is in C++ (and C) specification. Whether a standard library has the limitation has no effect on whether the library is standard compliant. Any code that depends on the non-existence of the limitation is not compliant with the standard.

and was [I] just lucky with the modern g++ version

One might say that you were unlucky. It was a stroke of luck when the program didn't work and you discovered the bug.

Second question, why does asking for the current position fix it?

I doubt that tellg is sufficient to make your program compliant with the standard. So, I would say that it "fixes" the program by chance.

You should probably be using std::flush(fs); instead.

this would mean that the NEW version is LESS standard compliant?

No, both versions of g++ are equally compliant in this regard. Your program isn't.

Community
  • 1
  • 1
eerorika
  • 232,697
  • 12
  • 197
  • 326