I am teaching C++ via Visual Studio and I would like to provide a portable, safe, modern System("pause")
like function. Reading How to simulate “Press any key to continue?” is rather frustrating as not a single answer provides a portable / modern solution that works.
The common suggestion: std::cin.get();
generally just doesn't do it:
- if
std::cin
was used before - it is skipped. So it needs to be called twice to pause once. - if written twice - and there was no input before - it will pause twice.
- if input earlier was invalid all calls are skipped.
std::cin.clear()
before doesn't solve it.
std::cin.ignore(std::numeric_limits<std::streamsize>::max(), '\n');
Is better than std::cin.get();
since calling std::cin.clear();
does in fact work for corrupted input (still causing a break).
Given that std::cin.ignore(...);
is skipped in an unpredictable manner is a problem. Note that this is also true for std::getline(std::cin, dummy);
and std::cin >> dummy;
(and of course std::cin.get();
.
Since std::cin
doesn't provide a check to see if it was used (like a flag) I have to see if the pause statement is skipped. One solution could be to use std::getline()
and look if the resulting std::string
is empty.
The problem:
user presses 'enter': the string is empty.
statement is skipped: the string is empty.
So this doesn't work. I can't see a way to implement this correctly without asking the user to input something other than enter.
I thought about this long and have one solution which is portable but hardware dependant: Messuring the time. typing in 'enter' takes considerably more time than beeing skipped. I say if the elapsed time is less than 20 microseconds I can assume it was skipped and ask for the second std::cin.ignore(...)
.
On my system the skipping never took more than 10 microseconds. The reaction time of humans will never exceed that - but this latency can be reduced when pressing 'enter' just before the function is called. In this quick case the lowest timeframe I got was 100 microseconds. So here's this implementation:
#include <iostream>
#include <limits>
#include <chrono>
void system_pause(unsigned max_keyboard_latency_hz = 50'000u) {
std::cout << "press enter to continue . . . ";
if (!std::cin.good()) {
std::cin.clear();
}
auto t1 = std::chrono::high_resolution_clock::now();
std::cin.ignore(std::numeric_limits<std::streamsize>::max(), '\n');
auto t2 = std::chrono::high_resolution_clock::now();
double delta_t = std::chrono::duration_cast<std::chrono::nanoseconds>(t2 - t1).count() / 1e9;
if (delta_t < 1.0 / max_keyboard_latency_hz) {
std::cin.ignore(std::numeric_limits<std::streamsize>::max(), '\n');
}
}
and max_keyboard_latency_hz
is the value (in hertz) where the first std::cin.ignore();
is considered skipped, if executed quicker. It behaves correctly after: input, corrupted input and no input. The problem is the extreme case (immediately pressing enter before) which is hardware dependant (perfomance / input latency) alltough the flexible max_keyboard_latency_hz
negates that a bit.
I would just like to know if there's a way to make a working System("pause");
like implementation which works for all three prior cases: input, no input and corrupted input. As far as my research goes there is no such solution so I will probably settle on my implementation (which is certainly not something to be happy about).