PDA

View Full Version : QTextStream input and output executing in wrong order



jrmrjnck
7th November 2010, 23:37
I have an odd problem where reading from stdin is always executing before writing to stdout (using QTextStream for both), regardless of their order in the code. This popped up in some code I was writing, but this reduced version below has the same problem:



#include <QTextStream>

int main( int argc, char* argv[] )
{
QTextStream cout( stdout, QIODevice::WriteOnly );
QTextStream cin( stdin, QIODevice::ReadOnly );

QString answer;
cout << "Answer: ";
cin >> answer;

return 0;
}

When it runs, it sits around waiting for input - only after I type something and press return does it output "Answer: ". I have tried many things to figure out what's happening:



if( argc == 2 ) {
cin >> answer;
}

doesn't fix it - it still asks for input first (unless argc != 2)




QString answer("wtf");
cout << "Answer: " << answer;
cin >> answer;
cout << endl << answer;

This is weird - it asks for the answer first, then outputs "Answer: wtf" followed by a newline and whatever input you gave it. Meaning that it reads stdin first, but it doesn't really store it in answer until it's supposed to. However, that's not entirely true:




QString answer("wtf");
cout << "Answer: " << answer;
cin >> answer;
if( answer == "asdf" ) {
cin >> answer;
}
cout << endl << answer;

This asks for input once and, only if you give it "asdf", asks a second time. Then it outputs "Answer: wtf" followed by a newline and whatever you told it the second time.

Building in debug, without optimizations, etc., makes no difference. This only happens on one computer/OS so I doubt it's reproducible for anyone else.

This is clearly not intended behavior and either a bug or configuration error on my part. I'm hesitant to install a different version of Qt as a few important things like KDE probably depend on it and it's currently working in all regards except for this odd problem. Using iostream is my workaround for the moment. Any suggestions?

Arch Linux x86_64 with KDE 5.4.2
Qt 4.7.0-4
gcc 4.5.1

Zlatomir
7th November 2010, 23:52
You can manually flush the stream:


cout << "Answer: ";
cout.flush();
//...

And i really don't recommend using the names exactly like the standard stream objects (cin, cout), because you might get some name clashes or confuse some juniors ;)

jrmrjnck
8th November 2010, 00:46
Thanks for your reply, that made it work.
However, I still don't understand what's going on. It hardly seems reasonable to have to flush the output buffer every time, especially if you have a lot of back and forth I/O on stdout.

Has this functionality changed between Qt versions - why haven't I noticed this before?
What exactly determines the order in which the I/O is printed - is it even deterministic?
Why does it "work" on some computers but not others?

I have much more experience with iostream than QTextStream (which is why I like cin and cout), and I've never encountered this. Does iostream handle buffering in a fundamentally different way, or does this same thing sometimes happen with iostream?

SixDegrees
8th November 2010, 00:48
Or you can try using cerr. cout is buffered by default; cerr is not, and output to cerr goes immediately to the stream. cout hangs on to output until some event - a filled buffer, a read request or an explicit flush - finally performs output.

jrmrjnck
8th November 2010, 03:04
Okay, so the buffering type explanation makes sense. I guess that different distributions might have different defaults for in/out/err, which are set in some other location (the kernel? libc?). Stderr on my system exhibited the same behavior as stdout (line buffering).

I also found you can flush the stream with a manipulator, as in

out << "Answer: " << flush;
which kind of takes care of my concern about writing too much code to do a simple thing.

Zlatomir
8th November 2010, 08:59
Your problem doesn't have anything to do with stdout (buffered) or stderr(not buffered).

Your issue is with the QTextStream (http://doc.trolltech.com/4.7/qtextstream.html#details) which is buffered. The flush in the code that i wrote is flushing the QTextStream buffer (not the stdout, your QTextStream has only the same name as std::cout, that is one reason i said you should use different names ;) )

jrmrjnck
8th November 2010, 16:33
So, please correct me if I'm wrong, but there is no way then to make QTextStream unbuffered, and the only way to reliably output to the console is to write a newline or flush the buffer. It seems true that the text is being held up at QTextStream as you state, since both QTextStream and iostream wrap the same stdout file handle, while iostream "works" for me.

That takes care of my problem, but I still wonder what would cause the same code to execute differently on different computers, and even change how it executes on the same system over time?