How do I implement a _getch() function instead of a cin function?

So I need help with a way to implement a _getch() function instead of a cin function in this Recursive-descent parsing calculator. I believe the program lays in this function ClassLexer() : m_stream{ std::cin } { } in the parser.h section of code. But due to the why the m_stream is implemented, i don't know how to replace it with the _getch() function.

Or maybe replace something in the tokenex.cpp file to get with a _getch() function in the fetch_next_token() function.

To clear the question. I need to use _getch() as the user input rather than cin.

I don't know how to approach this a really need some help. Thank you for the help in advance.

I'll provide my current code:

parser.h code:

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
  #pragma once
#include <iostream>
#include <map>
#include <string>
#include <cctype>

using namespace std;

namespace Lexer
{
    using variable_t = std::map<std::string, double>;

    // Kind is for token type
    enum class Kind : char
    {
        end, print = ';', plus = '+', minus = '-',
        mul = '*', div = '/', assign = '=',
        p_open = '(', p_close = ')',
        number, variable
    };

    struct Token
    {
        Kind kind;
        std::string name;
        double number;
    };

    class ClassLexer
    {
    private:
        std::istream& m_stream;
        Token m_current_token;

    public:

        ClassLexer() : m_stream{ std::cin } { }

        Token& get_current_token();

        Token fetch_next_token();

        std::istream& get_next_char(char& chr);

    };
}

namespace Parser
{
    using namespace Lexer;

    class ClassParser
    {
    private:
        ClassLexer m_lexer;
        variable_t m_variable;

    public:
        double expr(bool bNextToken);
        double term(bool bNextToken);
        double prim(bool bNextToken);

        void calculate();

    };
}


Tokenex.cpp code:

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
  #include "parser.h"

namespace Lexer
{
    std::istream& ClassLexer::get_next_char(char& chr)
    {
        while (this->m_stream.get(chr) &&
            (std::isblank(chr) || chr == '\n'));

        return this->m_stream;
    }

    Token& ClassLexer::get_current_token()
    {
        return this->m_current_token;
    }

    Token ClassLexer::fetch_next_token()
    {
        
        char lac = 0;

        
        this->get_next_char(lac);

        switch (lac)
        {
        case 0:
            this->m_current_token = { Kind::end };
            return this->m_current_token;

        case ';': case '+': case '-': case '*': case '/':
        case '=': case '(': case ')':
            this->m_current_token = { static_cast<Kind>(lac) };
            return this->m_current_token;

        case '0': case '1': case '2': case '3': case '4':
        case '5': case '6': case '7': case '8': case '9': case '.':
        {
        
            this->m_stream.putback(lac);

            this->m_stream >> this->m_current_token.number;

            this->m_current_token.kind = Kind::number;

            return this->m_current_token;
        }

        default:
        {
            if (std::isalpha(lac))
            {
                this->m_current_token.name = lac;

                while (this->m_stream.get(lac) &&
                    std::isalnum(lac))
                    this->m_current_token.name += lac;

                this->m_stream.putback(lac);

                this->m_current_token.kind = Kind::variable;
                return this->m_current_token;
            }
            else
            {
                this->m_current_token = { Kind::print };
                return this->m_current_token;
            }
        }

        } 

    } 
}


calcu.cpp code:

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
  #include "parser.h"

namespace Parser
{
    using namespace Lexer;

    double ClassParser::prim(bool bNextToken)
    {
        if (bNextToken)
            this->m_lexer.fetch_next_token();

        switch (this->m_lexer.get_current_token().kind)
        {
        case Kind::number:
        {
            double v = this->m_lexer.get_current_token().number;

            
            this->m_lexer.fetch_next_token();

            return v;
        }

        case Kind::variable:
        {
            double& v = this->m_variable[this->m_lexer.get_current_token().name];

            if (this->m_lexer.fetch_next_token().kind == Kind::assign)
                v = this->expr(true);

            return v;

        }
        case Kind::minus:
            return -this->prim(true);

        case Kind::p_open:
        {
            double v = this->expr(true);

            if (this->m_lexer.get_current_token().kind != Kind::p_close)
            {
                std::cerr << "Syntax error:\')\' is expected" << std::endl;
            }

            
            this->m_lexer.fetch_next_token();

            return v;
        }

        default:
        {
            std::cerr << "Syntax Error" << std::endl;
            return 0;
        }

        } 

    } 

    double ClassParser::term(bool bNextToken)
    {
        double left = prim(bNextToken);

        switch (this->m_lexer.get_current_token().kind)
        {
        case Kind::mul:
            left *= this->prim(true); return left;

        case Kind::div:
            left /= this->prim(true); return left;

        default:
            return left;
        }
    }

    double ClassParser::expr(bool bNextToken)
    {
        double left = this->term(bNextToken);
        switch (this->m_lexer.get_current_token().kind)
        {
        case Kind::plus:
            left += this->term(true); return left;

        case Kind::minus:
            left -= this->term(true); return left;

        default:
            return left;
        }
    }

    void ClassParser::calculate()
    {
        for (this->m_lexer.fetch_next_token();
            this->m_lexer.get_current_token().kind != Kind::end;
            this->m_lexer.fetch_next_token())
        {
            std::cout << this->expr(false) << std::endl;
        }

    }
}


main.cpp code:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
  #include "parser.h"

#include <conio.h>


void test_parse()
{
    Parser::ClassParser parser;

    parser.calculate();

}

int main()
{
    test_parse();
}
Why do you need _getch()? That function is platform specific, for Windows, though there is a POSIX alternative as well as a substitute in the <curses>/<ncurses> 3rd party library. <pdcurses> is an implementation for Windows.

I need to put _getch() in for the task. The task asks to create the calculator without first then a second part asks to do it with _getch(), but I don't know where or how to implement it into the program.

Do you know how to implement it?
You do realize the function is only useful for console I/O, right? Using it can cause problems with C++ stream-based I/O.

You don't need to "implement _getch," you just include a header file, one you've already included and the function is available for use. IF your compiler has the header available.

Your 'explanation' of the given task is still vague. Copy and paste ALL of your assignment, not just your interpretation.
The task is to create a 4 function calculator in the console I/O. But I need to user inputs be with a _getch() function not with cin, that I believe is being used? Or somehow have the _getch function as the new token but I don't know how to implement it.
That is all of the assignment? I doubt it.
The task is to use _getch in the program, that it. Could you please help me with finding a solution?
You aren't going to get any better answer here than at StackOverflow.
You are not supposed implement _getch() yourself, as it is provided by the runtime library and declared in the <conio.h> header file – but only on MS-DOS and Windows. As already pointed out, it is non-standard!

Why not use the standard getc(stdin) function to read the next character?
https://www.cplusplus.com/reference/cstdio/getc/
https://docs.microsoft.com/en-us/cpp/c-runtime-library/reference/getc-getwc?view=msvc-170
Last edited on
Duthomas wrote:
You aren't going to get any better answer here than at StackOverflow.

Someone is dangling hooks in multiple ponds, hoping there is some fish wanting to bite. Without any bait to entice.
Ronnie Coleman wrote:
The task is to use _getch in the program

FYI, using a function is entirely different than implementing a function. Implementing means you, the programmer, write the code for the function.

There are perfectly acceptable C functions that are not MS-DOS/Windows only. kigar64551 showed you one.

Another is getchar, it reads a single character from stdin only.
https://www.cplusplus.com/reference/cstdio/getchar/

There are examples how to use both functions at the links.

If you just have to use _getch here's an example how to use it:
https://docs.microsoft.com/en-us/cpp/c-runtime-library/reference/getch-getwch?view=msvc-170
Last edited on
OP has an XY problem and refuses to say much more than someone make this code work with _getch().
Ain't nothing but a peanut.
getc won't do it. It still requires you to press the enter key.
it is exceedingly difficult to replace getch with simple code such that it works like an arcade game input would want, eg hold down w to move forward, a for left sort of thing.
I don't know that he needs that for a calculator, but the trick is to decouple it from the buffered I/O so that it won't require the enter key to process it.

I tried for a while to do it without assembly or 3rd party, and never really got it working.
Last edited on
getc won't do it. It still requires you to press the enter key.


getc() simply reads the next character from the stream. Not more, not less 😀

When reading from the stdin in a console application – and if stdin was not redirected from a file – you are effectively reading from a pipe that is connected to the terminal. But when exactly does the terminal push the user's input into the the "write" end of the pipe? That's totally up to the terminal's inner workings!

You can not change this on "your" side, but you may be able to configure the terminal.

In Windows, a terminal has the flag ENABLE_LINE_INPUT set by default. This means the terminal itself waits until a full line is completed before passing the input on to the application! You can disable this behavior:

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
#include <stdio.h>
#include <io.h>
#include <ConsoleApi.h>

static BOOL disable_console_line_input_win32(FILE *const stream)
{
	const HANDLE con_handle = (HANDLE) _get_osfhandle(_fileno(stream)); // <-- that is how you obtain a "raw" Win32 handle from a FILE*
	DWORD mode;
	if (GetConsoleMode(con_handle, &mode)) // <-- get the current console mode first
	{
		return SetConsoleMode(con_handle, mode & (~ENABLE_LINE_INPUT));  // <-- update console mode with ENABLE_LINE_INPUT removed!
	}
	return FALSE;
}

int main()
{
	setvbuf(stdin, NULL, _IONBF, 0); // <-- just to be sure that there's no additional buffering in the C runtime I/O layer
	disable_console_line_input_win32(stdin); // <-- the real "magic" happens here!
	for (;;)
	{
		const char c = getc(stdin);
		printf("You typed: '%c'\n", c);
	}
	return 0;
}


Works fine for me, on Windows.

This clearly is very platform-specific. But I don't think it can be done with just C/C++ standard library functions. You'll have to resort to Win32 API on Windows, and probably to POSIX API on Linux/BSD systems.

For Linux, have a look at "Solution 2" here:
https://localcoder.org/linux-equivalent-for-conio-h-getch
Last edited on
The parser code is predicated upon using a stream for input (and one that allows putback() ). _getch() doesn't operate with streams. It obtains one char from the keyboard with no echo. >> isn't supported. So you would have to deal with stream extraction of numbers digit by digit etc.

If the task is actually to use _getch() (and I would really have to ask why this is), then possibly the simplest is to have an extra routine to obtain chars using _getch() until say a \n is found and use a stringstream to store the chars. A stringstream is just a stream and can be used like a file/cin stream. Note that in this case then normal 'editing' of input isn't done. You'll need to implement code for backspace (and any other line editing required). Using stream input (cin) then this is done for you.

See http://www.cplusplus.com/reference/sstream/stringstream/
Topic archived. No new replies allowed.