Re: Using std::cin.rdbuf()->in_avail()



Paul wrote:
I have a small command-line (console) application which, I thought, it would be good to allow users to pause and to resume - or exit. To control this, users will enter input at the command prompt. Actual processing occurs in a different thread.

Here is a fragment of the code accepting user input:

---------------------------------------------------
std::cout << "Enter choice: 'P' - pause, 'Q' - quit: ";

char ch;
while (!finish) {
if (std::cin.rdbuf()->in_avail()) {

Note that in_avail is useful for getting characters that the OS has transferred to cin, but which you haven't yet read. In effect, it tells you have many characters you are *guaranteed* to be able to read without cin blocking. However, it may be the case (and usually is in fact) that in_avail will report fewer characters than can be read without blocking. In particular, it will often report 0 even when there is some input waiting.

if (std::cin >> ch) {

You realise the above skips whitespace?

std::cin.sync(); // flush remaining characters

cin.sync() doesn't do anything. Instead, you should ignore the number of characters you want to flush. E.g. to flush remaining known buffered chars:

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

or to flush until the next newline character:
std::cin.ignore(
std::numeric_limits<std::streamsize>::max(),
static_cast<unsigned char>('\n')
);

To flush all other console input (e.g. unknown buffered chars) you can do:

FlushConsoleInputBuffer(GetStdHandle(STD_INPUT_HANDLE));

Is there a way to cause, say, some termination
code in the thread doing processing to unblock std::cin when it is no longer needed or is there perhaps a better way to use .in_avail()?

Usually, under these circumstances, you would wait for signals on two object, std::cin's Windows file handle (get it with GetStdHandle(STD_INPUT_HANDLE)) and an event object FINISHED. e.g.

HANDLE hFinished = CreateEvent(NULL, FALSE, FALSE, NULL);
//...
HANDLE hStdInput = GetStdHandle(STD_INPUT_HANDLE);
HANDLE const hArray[2] = {hFinished, hStdInput};
bool finished = false;
while (!finished)
{
DWORD ret = WaitForMultipleObjects(2, hArray, FALSE, INFINITE);
switch (ret)
{
case WAIT_OBJECT_0:
finished = true;
return; //finished
case WAIT_OBJECT_0 + 1:
{
char c;
if (std::cin.get(c))
{
//deal with c
}
//flush everything else?
std::cin.ignore(std::cin.rdbuf()->in_avail());
FlushConsoleInputBuffer(hStdInput);
}

break;
default:
//error handling
}
}

The code to exit should call:
SetEvent(hFinished);

Tom
.