Программа C ++ перестает выдавать консольный вывод при вводе в консоль

У меня есть программа на C ++ (MSVC 2017), которая постоянно выводит отладочную информацию через std :: cout. Однако иногда, когда я физически взаимодействую с консолью (например, случайно нажимаю на нее), она перестает производить вывод. Это означает, что ничего не печатается, хотя программа продолжает работать и завершает делать то, что делает правильно.

Любые идеи, как это исправить? Удаление буфера std :: cout с помощью «std :: cout.setf (std :: ios :: unitbuf);» не имеет никакого эффекта

Образец:

#include <iostream>

int main()
{
int i = 0;
while (true) {
i++;
if (i%100000000 == 0) std::cout << i++ << "\n";
}
return 0;
}

3

Решение

Это то, что я сделал, чтобы воспроизвести тест — написание mcve.cc:

#include <iostream>
int main()
{
for (char i = 0;; ++i) std::cout << (int)i << std::flush;
return 0;
}

Я скомпилировал и запустил в VS2013 (режим отладки). Это начало «взрывать» числа.

Я щелкнул в окне консоли, и вывод остановился (как описано в ОП). После нажатия ESC, Я ожидал большего числа, но ничего не произошло.

Я приостановил отладку и просмотрел стек вызовов, но не было ничего необычного. Шагнув немного, это даже выглядело так, как будто код все еще выполняется. (Подсчет i все еще произошло, как я мог видеть на дисплее Auto отладчика.)

Итак, я начал применять решение другого Q / A SO: Как отключить выбор пользователя в консоли Windows. Хотя, похоже, оно того стоило, MCVE. Итак, мне пришлось завершить его с использованием Google и 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;
}

Это сработало — QuickEdit отключен. (Нажатие в окне консоли больше не прекращало вывод.)

Однако без этого трюка это должно сработать. (Меня беспокоило, что я этого не понял.) Подумав немного, я пришел к просветляющей идее. Может ли быть так, что std::cout было bad() после QuickEdit?

Итак, я сделал третью версию. Поскольку я не мог использовать coutпоставить я модифицировал i который я мог смотреть в отладчике. (На самом деле, возвращение std::cout::good() был также отображен, но с назначением i это еще более показательно.)

#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;
}

После выбора QuickEdit и ESC, i был постоянно 0, Следовательно, другое исправление очевидно: std::cout должно быть clear()периодически редактировать:

#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;
}

Я не уверен, какое из обоих решений мне нравится больше:

  • Первый наименее инвазивен (просто дополнение к началу main()).
  • последний — чистый C ++ (без кода платформы), который я предпочитаю в целом.

Было бы интересно получить замечание по этому поводу не-Windows-платформ …

Я не могу вспомнить, чтобы я когда-либо видел такую ​​проблему QuickEdit в Linux (ни Irix, ни Solaris — операционные системы, которые я использовал однажды в прошлом). В этих системах выборки обрабатывались (в моем случае) Xterm / X11 — за рамками потокового ввода-вывода.

Итак, возможно ли, что std::cout становится плохо в этих системах (при условии, что в выводе не было ошибок кодирования)?


Наконец, я нашел портативный неинвазивный метод (за счет многопоточности):

#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;
}

Запускает дополнительный поток для периодической проверки / очистки std::cout, Это что-то еще, что должно быть добавлено к main() только (что я считаю «неинвазивным исправлением») — реальная база кода не нуждается в изменении.

Примечание: я немного сомневался, возможен ли одновременный доступ к std::cout безопасно (хотя я верил, что помню это). Что касается этого, я нашел еще один Q / A SO: Cout синхронизирован / потокобезопасен?. Согласно принятому ответу в этой ссылке, он гарантирован (или, по крайней мере, необходим), начиная с C ++ 11.

2

Другие решения

Других решений пока нет …

По вопросам рекламы [email protected]