This is what I did to reproduce the test – writing mcve.cc
:
#include <iostream>
int main()
{
for (char i = 0;; ++i) std::cout << (int)i << std::flush;
return 0;
}
I compiled and started in VS2013 (debug mode). It started to "blow" out numbers.
I clicked into Console window and the output stopped (as described by the OP). After pressing ESC, I expected more numbers but nothing happened.
I paused the debugging and looked through the call stack but there was nothing extra-ordinary. Stepping a bit, it even looked like the code is still executed. (The counting of i
still happened as I could see in the Auto display of the debugger.)
So, I started to apply the solution of another Q/A SO: How to disable user selection in Windows console. Though, it appeared to be of worth it was no MCVE. So, I had to complete it with use of google and MSDN:
#include <iostream>
#include <Windows.h>
int main()
{
// disable QuickEdit mode in Console
HANDLE hInput = GetStdHandle(STD_INPUT_HANDLE);
DWORD prev_mode;
GetConsoleMode(hInput, &prev_mode);
SetConsoleMode(hInput, prev_mode & ~ENABLE_QUICK_EDIT_MODE);
// start test
for (char i = 0;; ++i) std::cout << (int)i << std::flush;
// done (never reached)
return 0;
}
This worked – QuickEdit disabled. (Clicking into Console window didn't stop output anymore.)
However, without this trick it should work as well. (It was bothering me that I didn't understand this.) After thinking a while, I came to the enlighting idea. Could it be that the std::cout
was bad()
after QuickEdit?
So, I made a third version. As I couldn't use the cout
put I modified i
which I could watch in the debugger. (Actually, the return of std::cout::good()
was displayed as well but with assignment to i
it is even more illustrative.)
#include <iostream>
#include <Windows.h>
int main()
{
for (char i = 0;; ++i) {
if (!std::cout.good()) i = 0;
std::cout << (int)i << std::flush;
}
return 0;
}
After QuickEdit selection and ESC, the i
was constantly 0
. Hence, the other fix is obvious: The std::cout
should be clear()
ed periodically:
#include <iostream>
#include <Windows.h>
int main()
{
for (char i = 0;; ++i) {
if (!std::cout.good()) std::cout.clear();
std::cout << (int)i << std::flush;
}
return 0;
}
I'm not sure which of the both solutions I like more:
- The former is least invasive (just an addition to the beginning of
main()
).
- the latter is pure C++ (without platform specific code) which I prefer in general.
It would be interesting to get a remark about this concerning non-Windows platforms...
I cannot remember that I've ever seen such QuickEdit issue on Linux (nor Irix or Solaris – the OSes I've used once in the past). On that systems, selections were handled (in my case) by the Xterm/X11 – beyond the scope of stream I/O.
So, is it even possible that std::cout
becomes bad on that systems (assuming there were no encoding errors in the output)?
Finally, I found a portable non-invasive method (at the cost of multi-threading):
#include <atomic>
#include <iostream>
#include <thread>
int main()
{
// spawn extra thread to clean cout periodically
std::atomic<bool> exitThreadClearCOut = false;
std::thread threadClearCOut([&]() {
while (!exitThreadClearCOut) {
if (!std::cout.good()) std::cout.clear();
std::this_thread::sleep_for(std::chrono::milliseconds(100));
// 100 ms - nearly non-perceptable for humans but an "eternity" for modern CPUs
}
});
// start main work
for (char i = 0;; ++i) {
std::cout << (int)i << std::flush;
}
// finish/join thread to clean cout periodically
exitThreadClearCOut = true;
threadClearCOut.join();
// done
return 0;
}
It starts an extra thread to do the periodical check/clean of std::cout
. This is something else which has to be added to main()
only (what I consider as "non-invasive fix") – the actual code base doesn't need to be changed.
A note: I was a little bit in doubt whether concurrent access to std::cout
is safe (although I believed to remember it is). Regarding this, I found another Q/A SO: Is cout synchronized/thread-safe?. According to the accepted answer in this link, it is guaranteed (or at least required) starting with C++11.