Replacement for _getch()?

G

Geoff

The Microsoft _getch() function gets a character from the console
without echo. It does not require a carriage return. It returns the
character (int) read and does not have an error return. This makes it
very handy for console applications that want to wait for a single
keystroke from the user or for password inputs where echo is
problematic.

Is there a replacement for this non-standard function in C++?

std::cin.ignore() and std::cin.get() fail as direct replacements since
they require the enter key and not just any key be pressed.

Solutions involving ncurses or unistd.h are unsatisfactory for the
same reason conio.h is unsatisfactory.
 
Ö

Öö Tiib

The Microsoft _getch() function gets a character from the console
without echo. It does not require a carriage return. It returns the
character (int) read and does not have an error return. This makes it
very handy for console applications that want to wait for a single
keystroke from the user or for password inputs where echo is
problematic.

Is there a replacement for this non-standard function in C++?
No.

std::cin.ignore() and std::cin.get() fail as direct replacements since
they require the enter key and not just any key be pressed.

Solutions involving ncurses or unistd.h are unsatisfactory for the
same reason conio.h is unsatisfactory.

Why? When you do not have something then you write
the function you need on platform that lacks it using what it
has. It is often just a couple of lines of code and internet is
probably full of examples.
For example there is <termios.h> on Linux:

// written in common subset of C and C++:
#include <stdio.h>
#include <termios.h>
static struct termios old, new;

char _getch(void)
{
tcgetattr(0, &old); /* grab old terminal i/o settings */
new = old; /* make new settings same as old settings */
new.c_lflag &= ~ICANON; /* disable buffered i/o */
new.c_lflag &= ~ECHO; /* set echo mode */
tcsetattr(0, TCSANOW, &new); /* use these new terminal i/o settings now */
char ch;
ch = getchar();
return ch;
tcsetattr(0, TCSANOW, &old); /* Restore old terminal i/o settings */
}

Wrote that plus header and you can use your '_getch()'-using code without
changes on Linux.
 
G

Geoff


That's unfortunate.

Why are they unsatisfactory? Because they are equally non-standard C++
and non-portable. (Yes, I realize console I/O is intimately connected
to the implementation, which is why _getch exists. It's an ancient
MS-DOS'ism that stems from Borland and Lattice C and is an artifact of
Microsoft backward compatibility.)
When you do not have something then you write
the function you need on platform that lacks it using what it
has.

I was trying to avoid reinventing the wheel.
It is often just a couple of lines of code and internet is
probably full of examples.
For example there is <termios.h> on Linux:

// written in common subset of C and C++:
#include <stdio.h>
#include <termios.h>
static struct termios old, new;

char _getch(void)
{
tcgetattr(0, &old); /* grab old terminal i/o settings */
new = old; /* make new settings same as old settings */
new.c_lflag &= ~ICANON; /* disable buffered i/o */
new.c_lflag &= ~ECHO; /* set echo mode */
tcsetattr(0, TCSANOW, &new); /* use these new terminal i/o settings now */
char ch;
ch = getchar();
return ch;
tcsetattr(0, TCSANOW, &old); /* Restore old terminal i/o settings */
}

Wrote that plus header and you can use your '_getch()'-using code without
changes on Linux.

I was aware of this code and was considering using it, thanks for
validating my suppositions about this. The above example is flawed,
however. The functions return int, not char.

int _getch(void)
{
struct termios oldattr, newattr;
int ch;

tcgetattr( STDIN_FILENO, &oldattr );
newattr = oldattr;
newattr.c_lflag &= ~( ICANON | ECHO );
tcsetattr( STDIN_FILENO, TCSANOW, &newattr );
ch = getchar();
tcsetattr( STDIN_FILENO, TCSANOW, &oldattr );
return ch;
}


My goal is less about OS portability and more about standardizing the
code but this will have to do, I suppose.
 
G

Geoff


Obviously. But that's not a "replacement", is it?
It is a non-standard function in C, and isn't something provided by the
C++ either. It should work in C++ to, so it seems to be your best choice.

Yes, the leading underscore designates it as a compiler extension per
the standard requirements. Indeed, it does work for a Windows console
application in Microsoft C++ and it appears to be the only choice for
that functionality. The only "standard" alternative is cin.ignore()
which buffers and echoes characters until the return key is pressed.
The other alternative would be to make the same system call that
_getch() uses internally to do the action.

Reading Microsoft's source in getch.c, _getch obtains the console lock
and calls _getch_nolock() which manipulates the console mode via the
GetConsoleMode/SetConsoleMode API functions and then goes into an
infinite loop waiting on ReadConsoleInput(), then it resets the
console mode and returns the character received. So much for any
standards compliance.
 
N

Nobody

I was aware of this code and was considering using it, thanks for
validating my suppositions about this. The above example is flawed,
however. The functions return int, not char.

Also, it doesn't install any signal handlers so the terminal state won't
be restored if the process is suspended or terminated while the function
is waiting for a key press. And modifying the terminal settings affects
all processes which are connected to the terminal.

One of the main reasons why C++ doesn't standardise getch() is because
it's a DOS-ism, i.e. something which only makes sense on a "PC", and the
people responsible for standardising C++ don't appear to share the
misconception that "computer" and "PC" are synonyms.
 
A

Alf P. Steinbach

One of the main reasons why C++ doesn't standardise getch() is because
it's a DOS-ism, i.e. something which only makes sense on a "PC",

With block mode terminals I guess it won't make much sense. But then
e.g. iostreams don't make much sense for embedded programming either.
And I've used similar functions in many programming languages on a
variety of systems, including *nix, with a variety of terminals, since
the early 1980s, so it's not a function that's specific to PCs.

Just clearing up the technical.

But I guess that this noise is meant as a jab in the direction of
someone, trying to convey the misleading impression that that someone
(Tib? the OP?) does not have a properly wide view of computing:
and the
people responsible for standardising C++ don't appear to share the
misconception that "computer" and "PC" are synonyms.


- Alf
 
G

Geoff

Also, it doesn't install any signal handlers so the terminal state won't
be restored if the process is suspended or terminated while the function
is waiting for a key press. And modifying the terminal settings affects
all processes which are connected to the terminal.

This would seem to be an issue only with *nix then, since modifying a
Windows console only affects the instance of that application's
console, not all consoles. I can't vouch for what may happen if a
process is terminated but I believe Windows will clean it up. I
suppose that particular snippet assumes (and doesn't document that
assumption) that an instance handler would be defined.

That particular code sample goes back years to some pretty flippant
and flawed advice on several web forums on the topic of _getch
replacement so I'm not surprised it has issues.
One of the main reasons why C++ doesn't standardise getch() is because
it's a DOS-ism, i.e. something which only makes sense on a "PC", and the
people responsible for standardising C++ don't appear to share the
misconception that "computer" and "PC" are synonyms.

While it may be a DOS-ism the functionality of receiving a character
or string of characters without echoing them is universal, even when
there isn't a keyboard on the "computer".

My primary goal wasn't portability so much as "standardizing" a
particularly old program dating back to 1988 or so. Microsoft, in
their dedication to legacy functionality, conveniently provides
identical functionality in VB and C# as Console.ReadKey(true). I was
hoping for something similar in standard C/C++.
 
N

Nobody

With block mode terminals I guess it won't make much sense. But then e.g.
iostreams don't make much sense for embedded programming either. And I've
used similar functions in many programming languages on a variety of
systems, including *nix, with a variety of terminals, since the early
1980s, so it's not a function that's specific to PCs.

The functionality may not be specific to PCs, but the getch() API is. On
Unix (or other systems whose model of interactive input is based upon
dedicated terminals connected to serial ports), "read character without
echo" cannot be simplified to a single getch() function without being
oversimplified.

Configuring the terminal is a specific operation, as are reading
characters, restoring the terminal to its original state, and ensuring
that the terminal is restored upon suspend and reconfigured upon resume.

And iostreams are perfectly useful for embedded programming. "iostream"
doesn't automatically mean "fstream" (and plenty of embedded platforms
have filesystems).
 
A

Alf P. Steinbach

The functionality may not be specific to PCs, but the getch() API is. On
Unix (or other systems whose model of interactive input is based upon
dedicated terminals connected to serial ports), "read character without
echo" cannot be simplified to a single getch() function without being
oversimplified.

So?

Why would anyone would want just a single function?

Configuring the terminal is a specific operation, as are reading
characters, restoring the terminal to its original state, and ensuring
that the terminal is restored upon suspend and reconfigured upon resume.

That's what C++ constructors and destructors help to automate:
initialization and cleanup. Yay! :)

Anyway, I suggest you try the "curses" library (in one of its
incarnations), even if it's C language; IIRC it does much of this for you.

http://linux.die.net/man/3/getch

And iostreams are perfectly useful for embedded programming. "iostream"
doesn't automatically mean "fstream" (and plenty of embedded platforms
have filesystems).

So in your opinion I should have written "fstream", not "iostreams", yes?


Cheers, & have a nice weekend!,

- Alf
 

Ask a Question

Want to reply to this thread or ask your own question?

You'll need to choose a username for the site, which only take a couple of moments. After that, you can post your question and our members will help you out.

Ask a Question

Members online

Forum statistics

Threads
473,744
Messages
2,569,484
Members
44,903
Latest member
orderPeak8CBDGummies

Latest Threads

Top