Why does this file stream, with no error flags set, fail to read unless I do tellg()?

半世苍凉 提交于 2019-12-25 09:09:58

问题


I have a fstream that seems to get into a phantom failure state, even though examining it (via conversion to bool) reveals no error flags. Subsequent reads fail, which is unexpected.

#include <fstream>
#include <iostream>
#include <cassert>

int main()
{
    const unsigned int SAMPLE_COUNT = 2;

    // Setup - create file with two "samples"; each sample has a double and a float
    {
        std::ofstream ofs("/tmp/ffs", std::ios::binary);

        const double colA[SAMPLE_COUNT] = {  1.0,    2.0 };
        const float  colB[SAMPLE_COUNT] = { 42.0,  100.0 };

        for (size_t i = 0; i < SAMPLE_COUNT; i++) {
            ofs.write((char*)&colA[i], sizeof(colA[i]));
            ofs.write((char*)&colB[i], sizeof(colB[i]));
        }
    }

    // Actual testcase
    {
        std::fstream fs("/tmp/ffs", std::ios::binary | std::ios::out | std::ios::in);
        assert(fs);

        unsigned int sample_n = 0;
        while (true) {
            double a;
            fs.read((char*)&a, sizeof(a));

            if (!fs) {
                std::cerr << "No more samples\n";
                break;
            }

            std::cerr << "Sample " << (++sample_n) << " first column = " << a << '\n';


            // Read column B
            float b;
            fs.read((char*)&b, sizeof(b));
            assert(fs);
            std::cerr << "Current value second column = " << b << '\n';

            // Multiply it by two and write back
            b *= 2;
            fs.seekp(-sizeof(b), std::ios::cur);
            fs.write((char*)&b, sizeof(b));
            assert(fs);

            #ifdef DO_THE_TELLG
                // Unless I do this, the `fs.read` on the next iteration fails!
                // So the loop ends, and I get only the first sample transformed in my file.
                fs.tellg();
            #endif
        }
    }

    // Inspection - should see values 84 and 200, but see 84 and 100 instead.
    {
        std::ifstream ifs("/tmp/ffs", std::ios::binary);

        std::cerr << "All values at end:\n";
        for (size_t i = 0; i < SAMPLE_COUNT; i++) {

            double a;
            float  b;

            ifs.read((char*)&a, sizeof(a));
            ifs.read((char*)&b, sizeof(b));

            std::cerr << a << '\t' << b << '\n';
        }

        assert(ifs);
    }
}

Observe in the following output, only the first sample was "parsed", and so at the end the second sample retains its original value 100:

[root@localhost ~]# ./test
Sample 1 first column = 1
Current value second column = 42
No more samples
All values at end:
1       84
2       100

If I perform a tellg() or tellp() operation on it, the subsequent read succeeds, so the loop is not prematurely ended and the second sample is also multiplied by 2, to produce 200:

[root@localhost ~]# ./test
Sample 1 first column = 1
Current value second column = 42
Sample 2 first column = 2
Current value second column = 100
No more samples
All values at end:
1       84
2       200

This only occurs for me in the following environment:

  • CentOS 6 x86_64, GCC 4.4.7
  • CentOS 6 x86_64, GCC 4.8.2 (via devtoolset-2)

I get the expected behaviour, with or without tellg()/tellp(), on:

  • Coliru, GCC 6.3.0
  • CentOS 7

(Where a listed compiler supports both C++03 and C++11, I have tried it under both and observed no difference.)

Does my program have UB? Or, is this a libstdc++ bug that I need to work around?


Update: okay, so this is a known thing. But Dietmar doesn't say whether this is standard-mandated, or a libstdc++ bug. To me, it looks like bug 40732, but that was RESOLVED/WONTFIX, so why would my program work as expected under Coliru and CentOS 7? Ideally I'd like to get a better handle on precisely what's going on before putting this workaround in place.

来源:https://stackoverflow.com/questions/42116186/why-does-this-file-stream-with-no-error-flags-set-fail-to-read-unless-i-do-tel

易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!