Discussion:
Of getline(cin,...), Ctrl+D and infinite loops
(too old to reply)
a***@gmail.com
2007-04-24 21:40:19 UTC
Permalink
Here is a small program. It's meant to keep reading lines from the
stdin till someone types the (really cute) string "manchu". In that
case it breaks out of the loop and the program terminates.

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

int main() {
string str;
char ch;
while ( cin ) {
cin.clear();
cin.ignore( cin.rdbuf()->in_avail() );
getline( cin, str, '\n');
if ( str == "manchu" )
break;
if ( cin.eof() || cin.fail() ) {
cin.clear();
}
}

return 0;
}


I am compiling this with gcc 3.3.1 on Solaris 9 SPARC, and with gcc
3.2.3 on RHEL 3.

Now here is the crux. I wanted that if the user just typed "Ctrl+D",
it should clear the eof and fail bits and continue the loop looking
for more input. It does this nicely on Linux but goes into an infinite
loop on Solaris sparc, repeatedly encountering EOF. I am guessing that
this is a wrong piece of code
and somehow it is working in one case, and not in the other case.

And hoping that someone actually points out where I am going wrong.
--
[ See http://www.gotw.ca/resources/clcm.htm for info about ]
[ comp.lang.c++.moderated. First time posters: Do this! ]
Alberto Ganesh Barbati
2007-04-25 11:51:15 UTC
Permalink
Post by a***@gmail.com
And hoping that someone actually points out where I am going wrong.
The fact is that calling clear() only resets the status bits of the C++
stream object, but that may or may not affect the actual state of the
"controlled sequence", in this case the sequence of characters coming
from the console. What may be happening (and I stress *may* because we
are in realm of implementation-defined behaviour here) could be that by
pressing Ctrl+D the stdin stream is being closed. So even if call
clear() the stream will still remain closed (it's beyond the semantic of
clear() to reopen the stream): the next input operation will fail and
set again the eof+fail condition.

Ganesh
--
[ See http://www.gotw.ca/resources/clcm.htm for info about ]
[ comp.lang.c++.moderated. First time posters: Do this! ]
a***@gmail.com
2007-04-29 17:46:53 UTC
Permalink
{ Please include attributions for quotes (whom is quoted), and please
don't /remove/ them. -mod/aps }
Post by Alberto Ganesh Barbati
Post by a***@gmail.com
And hoping that someone actually points out where I am going wrong.
The fact is that calling clear() only resets the status bits of the C++
stream object, but that may or may not affect the actual state of the
"controlled sequence", in this case the sequence of characters coming
from the console. What may be happening (and I stress *may* because we
are in realm of implementation-defined behaviour here) could be that by
pressing Ctrl+D the stdin stream is being closed. So even if call
clear() the stream will still remain closed (it's beyond the semantic of
clear() to reopen the stream): the next input operation will fail and
set again the eof+fail condition.
Indeed, that is all cin.clear() is guaranteed to do. I had to provide
some form of a fix for this (the code I posted was just a small stub
capturing the essence of the actual code in my app). I saw that a call
to:

::clearerr(stdin);

following the cin.clear() call allows inputs to happen again. This is
far from an ideal situation - I would really want my program to choke
on EOF rather than ignore it and treat it as null input. But "the
powers that be" reckoned that ignoring EOF is better. It certainly was
less effort for me.

I am actually surprised that my earlier program was taking inputs on
Linux and AIX after cin.clear(). After running the following program
on Solaris, AIX, Linux and FreeBSD, with identical output everywhere,
I would have guessed that my original program should have choked on
Ctrl+D everywhere - because cin.clear() never resets the state of the
C stream stdin.:


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

int main()
{
string str;
getline( cin, str );

if ( !cin ) {
if ( feof(stdin) )
cout << "1. feof(stdin)" << endl;
if ( ferror(stdin) )
cout << "1. ferror(stdin)" << endl;
}
cin.clear();
{
if ( feof(stdin) )
cout << "2. feof(stdin)" << endl;
if ( ferror(stdin) )
cout << "2. ferror(stdin)" << endl;
}
clearerr(stdin);
{
if ( feof(stdin) )
cout << "3. feof(stdin)" << endl;
if ( ferror(stdin) )
cout << "3. ferror(stdin)" << endl;
}

return 0;
}


Now does that mean that the iostream implementations in some places
have nothing to do with the stdio streams and in other places it does?
--
[ See http://www.gotw.ca/resources/clcm.htm for info about ]
[ comp.lang.c++.moderated. First time posters: Do this! ]
Alberto Ganesh Barbati
2007-04-30 17:55:47 UTC
Permalink
Post by a***@gmail.com
Now does that mean that the iostream implementations in some places
have nothing to do with the stdio streams and in other places it does?
The standard says in 27.3.1/1: "The object cin controls input from a
stream buffer associated with the object stdin, declared in <cstdio>."
Similary for cout and cerr in subsequent paragraphs. So definitely there
shall be a relationship between cin/cout/cerr and their stdio counterparts.

Notice, however, that it does *not* say "The object cin controls input
from stdin". The precise choice of words allows (intentionally, I
presume) different implementation strategies. For example, cin might
hold a copy of stdin and achieve control of the "stream buffer" through
it, or both cin and stdin might control a shared "stream buffer" object.

Besides these arguments, as the standard does not specify a behaviour in
the situation you are describing, the "unspecified" behaviour definition
applies: the behaviour "depends on the implementation. The
implementation is not required to document which behavior occurs." (1.3.13).

HTH,

Ganesh
--
[ See http://www.gotw.ca/resources/clcm.htm for info about ]
[ comp.lang.c++.moderated. First time posters: Do this! ]
Ulrich Eckhardt
2007-04-25 11:50:58 UTC
Permalink
Post by a***@gmail.com
int main() {
string str;
char ch;
while ( cin ) {
cin.clear();
cin.ignore( cin.rdbuf()->in_avail() );
Please read up what in_avail() returns, there are basically three distinct
value categories:
1. Nothing.
2. Something.
3. Unknown.
You don't handle the third case here in any meaningful way!
Post by a***@gmail.com
getline( cin, str, '\n');
if ( str == "manchu" )
break;
The '\n' as delimiter is the default, but here is one real problem: you use
the content of str before checking if the input operation succeeded.
Post by a***@gmail.com
if ( cin.eof() || cin.fail() ) {
cin.clear();
}
You are clearing the streamstate here and at the beginning of the loop, why?
Post by a***@gmail.com
I wanted that if the user just typed "Ctrl+D", it should clear the eof
and fail bits and continue the loop looking for more input. It does
this nicely on Linux but goes into an infinite loop on Solaris sparc,
repeatedly encountering EOF. I am guessing that this is a wrong piece
of code and somehow it is working in one case, and not in the other
case.
Hmmm, this is mostly outside the control of C++. However, consider this case

./yourprog < inputfile

How would you want to reset the EOF when you reached the end of the file? I
guess that the input of Solaris simply goes EOF and that there is nothing
you can do against it - the bits in the IOStream are only reflections of
the 'true' stream's state, so clearing them doesn't change the facts
underneath.

Uli
--
Sator Laser GmbH
Gesch�ftsf�hrer: Ronald Boers, Amtsgericht Hamburg HR B62 932


[ See http://www.gotw.ca/resources/clcm.htm for info about ]
[ comp.lang.c++.moderated. First time posters: Do this! ]
a***@gmail.com
2007-05-01 19:38:30 UTC
Permalink
Post by Ulrich Eckhardt
Post by a***@gmail.com
int main() {
string str;
char ch;
while ( cin ) {
cin.clear();
cin.ignore( cin.rdbuf()->in_avail() );
Please read up what in_avail() returns, there are basically three distinct
1. Nothing.
2. Something.
3. Unknown.
You don't handle the third case here in any meaningful way!
What are these three cases?
Nothing - there are no leftover chars in the buffer.
Something - there are.
Unknown - Is this the case when gptr is NULL and showmanyc is called
on the streambuffer?

I am not so great at iostreams anyway [and I wish that the Langer/
Kreft book becomes available in my country pretty soon] - so I am
getting all kinds of responses to my use of:

cin.ignore( cin.rdbuf()->in_avail() );

Is this a generally bad strategy to follow? I am using it in a
terminal based app in a scenario where between successive user inputs,
there would be prompts coming about to indicate when the user needs to
make an input next. Is this a flawed approach.

- Arindam
--
[ See http://www.gotw.ca/resources/clcm.htm for info about ]
[ comp.lang.c++.moderated. First time posters: Do this! ]
Ulrich Eckhardt
2007-05-02 18:08:10 UTC
Permalink
Post by a***@gmail.com
Post by Ulrich Eckhardt
Post by a***@gmail.com
int main() {
string str;
char ch;
while ( cin ) {
cin.clear();
cin.ignore( cin.rdbuf()->in_avail() );
Please read up what in_avail() returns, there are basically three
1. Nothing.
2. Something.
3. Unknown.
You don't handle the third case here in any meaningful way!
What are these three cases?
Nothing - there are no leftover chars in the buffer.
Something - there are.
Unknown - Is this the case when gptr is NULL and showmanyc is called
on the streambuffer?
The last case is when the implementation does not determine the number of
available characters. The reasons for that could be that it either doesn't
care, i.e. the programmer was simply too lazy to do it or that it is simply
impossible to implement. In particular for stdin or similar non-file
streams it is impossible to tell how many characters can be read, the best
one could do is to return how many were buffered in advance. Since this is
by no means a guarantee for anything, I as a programmer of the streambuffer
also just wouldn't bother.
Post by a***@gmail.com
I am not so great at iostreams anyway [and I wish that the Langer/
Kreft book becomes available in my country pretty soon] - so I am
cin.ignore( cin.rdbuf()->in_avail() );
Is this a generally bad strategy to follow? I am using it in a
terminal based app in a scenario where between successive user inputs,
there would be prompts coming about to indicate when the user needs to
make an input next. Is this a flawed approach.
There are two approaches I usually take:
1. input, compute, output
You read from stdin, compute whatever you need and output the results to
stdout. This is the typical behaviour for Unix apps like grep and sed. If
you encounter an error or EOF, you terminate.
2. interactive
This intersperses output to a user and input, often looping. Here, I usually
use getline() to gather input and then use stringstreams to parse it.

You mentioned in another reply in this thread that "the powers that be" want
it to ignore EOF. What are the precise reasons? My point is that for case 1
above, terminating on EOF is mandatory while for case 2, a user pressing
control-D is also explicitly requesting a shutdown so there is also no
point in ignoring this request. In either case, trying to ignore EOF would
be a flawed approach, hence I ask what the rationale was for that decision.

Uli
--
Sator Laser GmbH
Gesch�ftsf�hrer: Ronald Boers, Amtsgericht Hamburg HR B62 932


[ See http://www.gotw.ca/resources/clcm.htm for info about ]
[ comp.lang.c++.moderated. First time posters: Do this! ]
Loading...