break a loop with a keyboard input

I have a problem. I want to stop a loop with an input but I want the loop goes on if there is no input entered by the user.

My program display a data window and refresh it at a 5 seconds interval. I want to stop this loop with an input from the keyboard. The problem is: cin or cin.get() wait for an input and stop the loop until it get an input.

Do someone know how to tell to cin to stop if nothing has been typed on the keyboard? I mean, I would wish my loop continue until a int or a char is typed by the user.

A good way would be:

if ("!cin.get() after a defined time interval")
flag=1;
else flag=0;
return flag:

But I don't know how to stop a cin if nothing as been typed. A better way would be to call a fonction designed to get a cin but the same problem happen: how to call a fonction at anytime while the program is running?

Thank you very much for your answers!
kbhit() is a non-portable function available I think on Borland compilers.

On Un*x, you have to mess with termios settings to make stdin non-blocking.
I'am using linux fedora and mandrake. So I must forget kbhit(), even if some DOS emulators can run it but I don't want that. I did some seaching about termios and it seem to be very complex to use.
If you have any experience with multi-threaded programming, you can easily spawn a new thread and let that one block waiting for the input while the original thread continues in the loop. When there's input, the second thread can update a variable which the original thread checks to decide if it should break or not.

To get this done there's the Posix threads on Linux (pthread). There are also a number of cross-platform thread libraries (SDL and Boost are two examples I'm familiar with and I've been happy with both on Linux).
Here is some code I wrote a few years back to enforce unbuffered stdin.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
#include <stdio.h>
#include <stdlib.h>
#include <sys/time.h>
#include <sys/types.h>
#include <unistd.h>
#include <fcntl.h>
#include <termios.h>

int main()
{
    struct termios oldSettings, newSettings;

    tcgetattr( fileno( stdin ), &oldSettings );
    newSettings = oldSettings;
    newSettings.c_lflag &= (~ICANON & ~ECHO);
    tcsetattr( fileno( stdin ), TCSANOW, &newSettings );

        while ( 1 )
        {
                fd_set set;
                struct timeval tv;

                tv.tv_sec = 10;
                tv.tv_usec = 0;

                FD_ZERO( &set );
                FD_SET( fileno( stdin ), &set );

                int res = select( fileno( stdin )+1, &set, NULL, NULL, &tv );

                if( res > 0 )
                {
            char c;
                        printf( "Input available\n" );
            read( fileno( stdin ), &c, 1 );
                }
                else if( res < 0 )
                {
                        perror( "select error" );
            break;
                }
                else
                {
                        printf( "Select timeout\n" );
                }
        }

    tcsetattr( fileno( stdin ), TCSANOW, &oldSettings );
        return 0;
}

Thanks for your answers!

I found a C++ thread implementation code on linuxselfhelp:

class Thread
{
public:
Thread();
int Start(void * arg);
protected:
int Run(void * arg);
static void * EntryPoint(void*);
virtual void Setup();
virtual void Execute(void*);
void * Arg() const {return Arg_;}
void Arg(void* a){Arg_ = a;}
private:
THREADID ThreadId_;
void * Arg_;

};

Thread::Thread() {}

int Thread::Start(void * arg)
{
Arg(arg); // store user data
int code = thread_create(Thread::EntryPoint, this, & ThreadId_);
return code;
}

int Thread::Run(void * arg)
{
Setup();
Execute( arg );
}

/*static */
void * Thread::EntryPoint(void * pthis)
{
Thread * pt = (Thread*)pthis;
pthis->Run( Arg() );
}

virtual void Thread::Setup()
{
// Do any setup here
}

virtual void Thread::Execute(void* arg)
{
// Your code goes here
}

It seem a good way to do what I want but it doesn't compile. Do I have to put specific thread libraries? They don't talk about that on the site.

Ok I know why, its a generic code.
Last edited on
Hi jayprog,
The code that you posted is a wrapper class for threading. If you want to use it, you will have to replace thread_create with your thread library's create function, and make any changes necessary if your thread library doesn't match the imaginary one in the example (for example, some libraries have different types for the thread entry point).

Don't forget to add the thread library to your linker settings, and include the header in your source.
Thank you JivanAmara!

But I would want to know if a thread library exist by default in the g++ compiler or I have to downlaod and install one like sdl or boost.

I'm using ROOT( a C++ object oriented software designed for physics data analysis). So, it would be hard to implement a library like boost in ROOT.

Maybe I will change my strategy and use the flags generated by my data acquisition software(MIDAS) to control my display. Not trivial but easier than multi-threaded programming I guess.
I'm havent read the whole discussion, so if i'm missing someting, sorry for that.

The solution on your question in the first post would be:
include <windows.h> and use while (!GetAsyncKeyState(VK_ESCAPE)). GetAsyncKeyState does wat it name tells: it checks the state of the key. It doesnt wait for userinput.

Hope that helps
Last edited on
You couldn't read eight posts? You do know the OP said he's using Linux?

Sorry I didn't see this before. Jsmith's answer is the best answer so far (meaning it works and it works for the right reasons --it just requires direct read()s).

There is no need to spawn extra threads just for input handling. Idling a child thread hurts execution just as much as idling the main thread.

NCurses
If you can afford to get input somewhere other than the standard streams (cin), then you should seriously consider using NCurses ( http://www.gnu.org/software/ncurses/ ) or some other keyboard input library. Chances are you will have to use your package manager to download and install the proper libraries:

sudo apt-get install ncurses-dev

ought to do it (or something similar).

Terminal Settings
If you are required to stick with the standard I/O streams, you will want to play with the input stream directly. And yes, it is a whole can of worms.

Keep in mind that the following code snippit only works on modern POSIX systems (like Unix: AIX, Sun OS, etc and Linux and Mac OS X). You can do the same things on Windows... it just has to be done a different way.
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
#include <cstdio>
#include <iostream>
#include <limits>
#include <string>

#include <unistd.h>
#include <termios.h>
#include <poll.h>

static bool initialized = false;
static struct termios initial_settings;

//---------------------------------------------------------------------------
bool initialize()
  {
  if (!initialized)
    {
    initialized = (bool)isatty( STDIN_FILENO );
    if (initialized)
      initialized = (0 == tcgetattr( STDIN_FILENO, &initial_settings ));
    if (initialized)
      std::cin.sync_with_stdio();
    }
  return initialized;
  }

//---------------------------------------------------------------------------
void finalize()
  {
  if (initialized)
    tcsetattr( STDIN_FILENO, TCSANOW, &initial_settings );
  }

//---------------------------------------------------------------------------
bool linebuffered( bool on = true )
  {
  struct termios settings;

  if (!initialized) return false;

  if (tcgetattr( STDIN_FILENO, &settings )) return false;

  if (on) settings.c_lflag |= ICANON;
  else    settings.c_lflag &= ~(ICANON);

  if (tcsetattr( STDIN_FILENO, TCSANOW, &settings )) return false;

  if (on) setlinebuf( stdin );
  else    setbuf( stdin, NULL );

  return true;
  }

//---------------------------------------------------------------------------
bool echo( bool on = true )
  {
  struct termios settings;

  if (!initialized) return false;

  if (tcgetattr( STDIN_FILENO, &settings )) return false;

  if (on) settings.c_lflag |= ECHO;
  else    settings.c_lflag &= ~(ECHO);

  return 0 == tcsetattr( STDIN_FILENO, TCSANOW, &settings );
  }

//---------------------------------------------------------------------------
#define INFINITE (-1)

bool iskeypressed( unsigned timeout_ms = 0 )
  {
  if (!initialized) return false;

  struct pollfd pls[ 1 ];
  pls[ 0 ].fd     = STDIN_FILENO;
  pls[ 0 ].events = POLLIN | POLLPRI;
  return poll( pls, 1, timeout_ms ) > 0;
  }

//---------------------------------------------------------------------------
using namespace std;
int main()
  {
  if (!initialize())
    {
    cout << "You must be a human to use this program.\n";
    return 1;
    }

  try {
    string name, password;

    linebuffered( false );
    echo( false );

    cout << "Press any key to wake me up.\n";
    while (true)
      {
      cout << "Zzz..." << flush;
      if (iskeypressed( 500 )) break;
      }
    if (cin.get() == '\n') cout << "\nHmm... Not much of an 'any' key"
                                   " test when you press ENTER...\n";
    else                   cout << "\nThanks!\n";

    echo();
    linebuffered();
    cout << "\nPlease enter your name> " << flush;
    getline( cin, name );

    cout << "Please enter a (fake) password> " << flush;
    echo( false );
    getline( cin, password );
    echo();
    cout << "\nSo, your password is \"" << password << "\", hmm?\n";

    cout << "\nPress ENTER to quit> " << flush;
    cin.ignore( numeric_limits <streamsize> ::max(), '\n' );
	
    cout << "\nGood-bye " << name << ".\n";
    }
  catch (...) { }

  finalize();
  return 0;
  }

Again, the poll() function is available on most modern systems. If it doesn't have poll(), you can use the old BSD select() as jsmith did above.

The above leaves the terminal in a relatively "cooked" state: you can still use Ctrl-C to kill the program, Ctrl-S/Q to stop/start, Ctrl-Z; pressing ENTER will still be understood as '\n', etc. If you need to handle things like arrow keys and the like, that is a deeper can of worms: use NCurses.

Final Notes
you must call finalize() before terminating or your users will hate you. If this is homework then the above should be good enough (and your professor will know that you got this code from the internet, so own-up to it right up front). If this is a professional application, then you should already have some signal handlers (with sigaction()) in there to catch abnormal program termination. Be sure to finalize() from them too.

Hmm, I don't think I've forgotten anything...

Hope this helps.

[edit] Added INFINITE timeout, and a default timeout of zero milliseconds. (Sorry I forgot that...)
Last edited on
Sorry, when i posted my answer i was in a rush. I didnt see he was using Linux. I was come across this problem (on windows) just before i posted, and i couldnt find this solution in the posts before, so i tought i could help.

Next time when i dont have the time, i wont post :)
Actually, I've got to apologize for my own grouchiness --it was unwarranted.

For the equivalent to poll()ing the keyboard on Windows, use:
1
2
3
4
5
6
7
bool iskeypressed( unsigned timeout_ms = 0 )
  {
  return WaitForSingleObject(
    GetStdHandle( STD_INPUT_HANDLE ),
    timeout_ms
    ) == WAIT_OBJECT_0;
  }

<windows.h> #includes the necessary headers to #define INFINITE as the appropriate value, so this function can be used identically to the one above.

Enjoy!
Topic archived. No new replies allowed.