Google Groups no longer supports new Usenet posts or subscriptions. Historical content remains viewable.
Dismiss

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

444 views
Skip to first unread message

Paul

unread,
Jan 8, 2007, 1:40:44 PM1/8/07
to
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()) {
if (std::cin >> ch) {
std::cin.sync(); // flush remaining characters

switch (ch) {
case 'P':
case 'p':
do_process.stop();
std::cout << "Enter choice: 'R' - resume, 'Q' - quit: ";
break;
case 'R':
case 'r':
do_process.start();
std::cout << "Enter choice: 'P' - pause, 'Q' - quit: ";
break;
case 'Q':
case 'q':
finish = true;
return;
default:
std::cerr << "Unknown choice. Try again.\n";
}
}
else
std::cerr << "Error reading input.\n";
}
else
::Sleep(1000UL);
}

---------------------------------------------------

When instead of

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

I had

char ch;
while (std::cin >> ch) {

the code worked fine with the sole exception that if the user did not press
anything and let the applicaiton run to the end, on its termination he would
still have to press something, since "std::cin >> ch" would block. This was
the problem I tried to avoid by using std::cin.rdbuf()->in_avail(); however,
the code as written does not take any input until after the thread exits
when this becomes useless. 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()?

Thank you.

Paul


Ulrich Eckhardt

unread,
Jan 9, 2007, 3:29:33 AM1/9/07
to
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.

That's what signals or flow control are for... though I'm not sure they work
sufficiently under win32.

> std::cout << "Enter choice: 'P' - pause, 'Q' - quit: ";
>
> char ch;
> while (!finish) {
> if (std::cin.rdbuf()->in_avail()) {

1. in_avail() does not guarantee that no characters are available when it
doesn't say that any are available. Only if it says there are some there
are some.
2. It is a function of the streambuffer, which is more low-level than the
stream itself - we will come to the meaning of that soon.

> if (std::cin >> ch) {

Okay, unless you explicitly excluded that, this function will read the first
non-whitespace (!!) character into 'ch'. If there is only whitespace,
in_avail() will happily say there is input while this here will block.

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

No, that's not what this does, use ignore().

> the code worked fine with the sole exception that if the user did not
> press anything and let the applicaiton run to the end, on its termination
> he would still have to press something, since "std::cin >> ch" would
> block. This was the problem I tried to avoid by using
> std::cin.rdbuf()->in_avail(); however, the code as written does not take
> any input until after the thread exits when this becomes useless. 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()?

A raw way would be to simply exit() from the thread, taking the whole
program with it.

Other than that, you will have to revert to low-level keyboard handling
routines, C++ IOStreams simply don't work like that. The first approach is
to poll getch() and the flag that signals the termination of the worker
thread. A more elaborate approach would be to use select().

Uli

Charles Wang[MSFT]

unread,
Jan 11, 2007, 6:35:16 AM1/11/07
to
Hi,
This is a quick note to let you know that I am performing research on this
issue. I will let you know the result as soon as possible.
If you could send me (chan...@microsoft.com) your email address, I will
timely let you know the result.
Sorry for bringing you any inconvenience.

Charles Wang
Microsoft Online Community Support

======================================================
When responding to posts, please "Reply to Group" via
your newsreader so that others may learn and benefit
from this issue.
======================================================
This posting is provided "AS IS" with no warranties, and confers no rights.
======================================================


Tom Widmer [VC++ MVP]

unread,
Jan 15, 2007, 8:10:15 AM1/15/07
to
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

Charles Wang[MSFT]

unread,
Jan 16, 2007, 11:25:16 AM1/16/07
to
Hi Paul,
I am sorry for this late response due to asking for two days sick leave.

From my research, I just found one way to resolve this issue.
The basic idea is as following:
1. Use a global flag to represent one thread execution status;
2. Use another thread to monitor this flag;
3. Exit the process if the flag has been changed to finished status.

Please refer to the following code:
#include <windows.h>

static int flag = 0;

class CWorker
{
private:
HANDLE m_handle;
DWORD m_thdId;
LPTHREAD_START_ROUTINE m_thd_routine;
public:
CWorker(LPTHREAD_START_ROUTINE thdproc)
{
m_thd_routine = thdproc;

}

HANDLE getThreadHandle()
{
return m_handle;
}

void start(){
m_handle = CreateThread(NULL,0,m_thd_routine,NULL,NULL,&m_thdId);
}

void stop(){
SuspendThread(m_handle);
}

void resume()
{
ResumeThread(m_handle);
}

void exit()
{
DWORD nCode = 0;
GetExitCodeThread(m_handle, &nCode);
CloseHandle(m_handle);
ExitThread(nCode);
}

};

DWORD WINAPI Thread1(LPVOID lpParameter)
{
Sleep(5000);
std::cout<<"Thread1 ended!"<<endl;
flag = 1;
return 0;
};

DWORD WINAPI MonitorForExit(LPVOID lpParameter)
{
while(1)
{
if(flag==1)
ExitProcess(0);
}
return 0;
};

void main(int argc, char* argv[])
{
MSG msg;
char ch;
CWorker workerTask(Thread1);
CWorker workerMonitor(MonitorForExit);
workerMonitor.start();
while (std::cin >> ch) {
switch(ch)
{
case 's':
case 'S':
workerTask.start();
break;
case 'P':
case 'p':
workerTask.stop();

std::cout<< "Enter choice: 'R' - resume, 'Q' - quit: ";
break;
case 'R':
case 'r':

workerTask.resume();


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

return;
default:
std::cerr << "Unknown choice. Try again.\n";
}
}

}

Hope this helps. Please feel free to let me know if you have any other
questions or concerns.
It is always my pleasure to be of assistance.

Sincerely yours,

Paul

unread,
Jan 21, 2007, 3:28:49 PM1/21/07
to
>> if (std::cin >> ch) {
>
> You realise the above skips whitespace?

Yes.

>
>> std::cin.sync(); // flush remaining characters
>
> cin.sync() doesn't do anything.

Surprisingly, it helped, although I accept that its behaviour is
unspecified. Since I was reading only a character at a time (std::cin >>
ch), the new-line character remained unread and interfered with processing
later on whilst .sync() appeared to clear that. My logic at the time was
that since sync() is supposed to work for std::istream like .flush() for
std::ostream and since the latter flushes whatever we have to the real
destination, sync() would synchronise the internal buffer with the real
input source, i.e. clear the buffer. But I turned to using
std::cin.ignore(std::cin.rdbuf()->in_avail()).

Thank you. I did not realise I could wait on STD_INPUT_HANDLE.

Paul


Tom Widmer [VC++ MVP]

unread,
Jan 23, 2007, 6:00:36 AM1/23/07
to
Paul wrote:
>>> if (std::cin >> ch) {
>> You realise the above skips whitespace?
>
> Yes.

That means it reads an arbitrary number of characters, and thus may
block (e.g. if only whitespace has been entered).

>>> std::cin.sync(); // flush remaining characters
>> cin.sync() doesn't do anything.
>
> Surprisingly, it helped, although I accept that its behaviour is
> unspecified.

That's interesting - I haven't tried it with recent compilers, though it
didn't work in the past.

Since I was reading only a character at a time (std::cin >>
> ch), the new-line character remained unread and interfered with processing
> later on whilst .sync() appeared to clear that. My logic at the time was
> that since sync() is supposed to work for std::istream like .flush() for
> std::ostream and since the latter flushes whatever we have to the real
> destination, sync() would synchronise the internal buffer with the real
> input source, i.e. clear the buffer. But I turned to using
> std::cin.ignore(std::cin.rdbuf()->in_avail()).

That's definitely safer I think.

Tom

Paul

unread,
Jan 23, 2007, 6:48:03 PM1/23/07
to
Thank you, Tom.

Paul


0 new messages