2

Can feof(stdin) and cin.eof() produce discordant results? I would expect them not to do so, but I guess I am missing something. Does the standard say anything about this?

Consider the following example, when no input is given. This can either be done by typing the shortcut for EOF in the terminal, or by redirecting the input on /dev/null (Unix), or with empty stdin sequence on ideone.com...

#include <cstdio>
#include <iostream>

int main() {
    using namespace std;

    char ch;
    cin >> ch;

    cin.clear();

    cout << boolalpha
         << "feof(stdin): " << bool(feof(stdin)) << endl
         << "cin.eof(): " << cin.eof() << endl;
}

Output:

feof(stdin): true
cin.eof(): false
effeffe
  • 2,821
  • 3
  • 21
  • 44
  • Please read this article https://stackoverflow.com/questions/5605125/why-is-iostreameof-inside-a-loop-condition-considered-wrong Although it is not a direct answer on your question, but it contains all information corresponding to answer on your question. – 273K Apr 17 '18 at 14:32
  • 1
    Doesn't `cin.clear();` [clear the EOF bit](http://en.cppreference.com/w/cpp/io/basic_ios/clear) for `cin`? Indeed, if I remove it, [I get the result you expect](https://ideone.com/Sh9rmd). – Fred Larson Apr 17 '18 at 14:33
  • @S.M. I am sorry, how is that related? – effeffe Apr 17 '18 at 14:36
  • @FredLarson It does, but [`std::cin` should be associated with `stdin`](http://en.cppreference.com/w/cpp/io/cin), which is why I would expect them to be "synchronized". Otherwise it looks like I could read from `std::cin` but not from `stdin`... – effeffe Apr 17 '18 at 14:40
  • `feof(3)` is a C function, so it is not included in the C++ spec, but for legacy purposes. – Luis Colorado Apr 18 '18 at 17:37
  • @LuisColorado The C standard library *is* part of the C++ standard library: the C++ specification refers to it (rather than specifying its behavior itself) just to avoid repetition. https://timsong-cpp.github.io/cppwp/library.c – effeffe Apr 18 '18 at 17:42

1 Answers1

2

Well, there is no surprise here.

std::cin is truely defined in the C++ library, but stdin is defined in the C standard library that is member of the C++ one mainly for compatibility reasons.

Draft n4659 for C++17 says:

30.4.3 Narrow stream objects [narrow.stream.objects]

istream cin;

1 The object cin controls input from a stream buffer associated with the object stdin, declared in (30.11.1).

and later:

30.11 C library files [c.files]
30.11.1 Header <cstdio> synopsis [cstdio.syn]
...
1 The contents and meaning of the header <cstdio> are the same as the C standard library header <stdio.h>.

That means that cin is a C++ stream wrapping the underlying stdin C FILE object. As a result, when you try to read a character from cin after reaching the end of file, cin will ask it from stdin, stdin will report an end of file condition and both cin and stdin will set their internal end of file flag. Then the call to cin.clear() clears the C++ stream flag but it is unspecified whether it has an action on the stdin object internal flag.

So you are responsable for the lack of synchronization between the C++ cin object and the C stdin one.


Now for the details on what happens in this code copied from your ideone:

#include <cstdio>
#include <iostream>

// first type the shortcut for EOF, then a single character

int main() {
    using namespace std;

    char ch;
    cin >> ch;             // 1

    cin.clear();           // 2

    cout << boolalpha      // 3
         << "feof(stdin): " << bool(feof(stdin)) << endl
         << "cin.eof(): " << cin.eof() << endl;

    ch = char(getchar());  // 4
    cout << ch << endl;    // 5

    cout << boolalpha      // 6
         << "feof(stdin): " << bool(feof(stdin)) << endl
         << "cin.eof(): " << cin.eof() << endl;
}

//1 : you read a char from cin (ignoring the char): cin internally read from stdin, stdin finds an end of file, sets its internal EOF flag and return an end of file condition to cin. cin sets its own EOF flag

//2 : you clear the EOF flag on cin, stdin is unchanged (in this implementation since behaviour is unspecified by standard AFAIK)

//3 : as expected feof(stdin) is true and cin.eof() is false

//4 : you read a char from stdin (cin is untouched here). As you are already at end of file, getchar returns the integer constant EOF = -1. But as you force a conversion to char -1 is converted to '\xff', ASCII char DEL

//5 : '\xff' is not a valid UTF8 sequence and ideone displays it as the unicode REPLACEMENT CHARACTER �

//6 : feof(stdin) is still true: the C FILE has reached end of file, and cin.eof() is still false because nothing changed it since //3

TL/DR: everything is as expected: the underlying file as reached end of file and getchar() does return EOF, and the eof flags are set. Simply as you explicitely clear cin flags and never use it later cin.eof() is false.

Serge Ballesta
  • 143,923
  • 11
  • 122
  • 252
  • Hm, would now need to conclude that once EOF was received from console, with each new read via stdin we would get eof-flag reset even after `clearerr(stdin)`, otherwise we'd easily get further inconsistent behaviour... Is the standard (C now, presumably...) precise about? – Aconcagua Apr 17 '18 at 15:17
  • It seems to me that after `cin.clear()`, `stdin` is left in an inconsistent state: its EOF flag is on, but I *can* read (both through `std::cin` and directly from `stdin`)... furthermore, after such successful read, the flag is still on! I am not sure whether this is what @Aconcagua is talking about, but it looks more like an inconsistency problem to me rather than a synchronization one. [Here](https://ideone.com/JrgOIc) is a MWE, though you have to run it in a terminal to give EOF and then more input. – effeffe Apr 17 '18 at 16:06
  • @effeffe If you can read even with eof flags set, looks pretty much as if EOF is not meaningful on console, and thus the flags not either... – Aconcagua Apr 17 '18 at 16:27
  • @Aconcagua The C standard explicitly says that, if the EOF flag is set, functions reading a character should return `EOF`. Firthermore, I do not see any exception to the rule regarding `stdin`. – effeffe Apr 17 '18 at 16:55
  • @effeffe ??? Seems as if we have a different definition of "being able to read". Receiving EOF in my eyes is no successful read... – Aconcagua Apr 17 '18 at 20:33
  • @Aconcagua The read *is* successful, it returns the correct characer, but the EOF flag is set, both before and after it. You can try the snippet above. – effeffe Apr 18 '18 at 07:57
  • @effeffe Uh oh. Concatenating your last two comments together then means that your C++ implementation is not standard conform... – Aconcagua Apr 18 '18 at 08:01
  • Let us [continue this discussion in chat](https://chat.stackoverflow.com/rooms/169222/discussion-between-effeffe-and-aconcagua). – effeffe Apr 18 '18 at 08:05
  • Regarding your last edit: what you describe is *not* the behavior I get. `getchar` returns the correct character I type. – effeffe Apr 18 '18 at 08:12
  • Thinking about it, the consequence of your line of thought seems to be that all the flags of the streams wrapping stdin/out/err are basically useless, as well as all the operations manipulating them, right? If these flags do not match the state of the underlying C object on which the actual I/O is made, then what is their meaning? Also, it would mean that it is not possible to recover from error/EOF with such streams without using the C library... am I missing something? – effeffe Apr 18 '18 at 19:16