【问题标题】:Exit loop without waiting for the iteration to end退出循环而不等待迭代结束
【发布时间】:2021-12-15 08:02:52
【问题描述】:

我正在尝试制作一个简单的控制台计算器。它按预期工作,但是,我想通过按下按钮退出程序并且程序不等待循环结束。我尝试将!(GetAsyncKeyState(VK_RSHIFT)) 放在while 循环条件中,我也尝试将!(GetAsyncKeyState(VK_RSHIFT)) 放在循环中的if 语句中,结果是一样的。

#include <iostream>
#include <Windows.h>

using namespace std;

int main() {
    cout << "Press RSHIFT for the program to close!\n";

    while (!(GetAsyncKeyState(VK_RSHIFT))) {
        long double num1, num2;
        char mathOp;
        cin >> num1 >> mathOp >> num2;

        switch (mathOp) {
        case('+'):
            cout << num1 + num2 << endl;
            break;
        case('-'):
            cout << num1 - num2 << endl;
            break;
        case('*'):
            cout << num1 * num2 << endl;
            break;
        case('/'):
            cout << num1 / num2 << endl;
            break;
        default:
            cout << "ERROR: Operator doesn't match available operators (+ - * /)!\n";
        }
    }
    return 0;
}

【问题讨论】:

  • break 不会让你跳出循环,但 goto 会
  • 您应该检查what GetAsyncKeyState returns。你的代码写得好像它返回一个bool,但它比这更复杂。
  • 当程序在cin &gt;&gt; num1 &gt;&gt; mathOp &gt;&gt; num2; 中等待用户输入时,不会调用其他函数。如果你真的需要这种行为,你必须创建一个单独的 UI 线程并在那里监控按键。一旦按下该键,您就可以拨打ExitProcess()
  • @Eugene 我想我可以在循环结束时使用睡眠功能,让用户有时间关闭程序。尽管我的解决方法不能完全回答我的问题,但它工作得还不错!感谢您的回复!但是,我将保持线程打开,以防有人知道在没有完成迭代的情况下退出循环的方法。
  • @Eugene:对我来说,使用ReadConsoleInput 而不是创建新线程似乎更合适。请参阅我的答案以获取更多信息。

标签: c++ windows loops winapi input


【解决方案1】:

函数调用

GetAsyncKeyState(VK_RSHIFT)

将在调用函数时报告 SHIFT 键的状态。但是,这似乎不是您想要的。

您似乎想要的是进行阻塞函数调用

cin &gt;&gt; num1 &gt;&gt; mathOp &gt;&gt; num2;

当用户按下 SHIFT 键时立即中止,以便您可以退出程序。

这在使用 C++ 标准库的std::cin 时是不可能的。

但是,您可以调用特定于平台的函数ReadConsoleInput 来等待键盘事件,而不是使用std::cin。如果用户按下 SHIFT 键,那么您可以停止程序。如果用户按下不同的键,那么您可以将该字符添加到输入缓冲区,当用户按下 ENTER 键时,您可以处理该输入缓冲区。

这样,当用户按下 SHIFT 键时,您的程序将立即收到通知,您不必求助于不可靠的变通方法,例如调用 Sleep 函数。

但是,此解决方案需要您在低级别处理输入,这需要您的程序执行一些通常会自动完成的额外工作,例如将输入回显到屏幕并为 提供特殊处理BACKSPACEENTER 键。

我在下面写了这样一个程序:

#include <Windows.h>
#include <iostream>
#include <string>
#include <sstream>

void process_input( const std::string &line )
{
    std::stringstream ss( line );

    long double num1, num2;
    char mathOp;
    ss >> num1 >> mathOp >> num2;

    if ( !ss )
    {
        std::cout << "Error parsing input line \"" << line << "\"\n" << std::flush;
        return;
    }

    switch (mathOp)
    {
    case('+'):
        std::cout << num1 + num2 << '\n';
        break;
    case('-'):
        std::cout << num1 - num2 << '\n';
        break;
    case('*'):
        std::cout << num1 * num2 << '\n';
        break;
    case('/'):
        std::cout << num1 / num2 << '\n';
        break;
    default:
        std::cout << "Unknown operator encountered in line \"" << line << "\"\n";
    }

    std::cout << std::flush;
}

int main( void )
{
    HANDLE hStdIn  = GetStdHandle( STD_INPUT_HANDLE );
    INPUT_RECORD ir;
    DWORD dwRead;

    std::string line;

    //get scan code of the right shift key
    UINT uRShiftScanCode = MapVirtualKeyA( VK_RSHIFT, MAPVK_VK_TO_VSC );

    for (;;)
    {
        if ( !ReadConsoleInputA( hStdIn, &ir, 1, &dwRead ) )
        {
            std::cout << "Error in ReadConsoleInput\n";
            std::exit( EXIT_FAILURE );
        }

        if ( ir.EventType != KEY_EVENT || !ir.Event.KeyEvent.bKeyDown )
            continue;

        if ( ir.Event.KeyEvent.wVirtualScanCode == uRShiftScanCode )
        {
            std::cout << "\n\nRight shift press detected, exiting program!\n";
            return 0;
        }

        switch ( ir.Event.KeyEvent.uChar.AsciiChar )
        {
        case 0: //keystroke does not translate to a valid character

            break;

        case '\r': //carriage return character

            //echo input to output
            std::cout << '\n' << std::flush;

            process_input( line );
            line.clear();
            break;

        case '\b': //backspace character

            for ( WORD i = 0; i < ir.Event.KeyEvent.wRepeatCount; i++ )
            {
                //echo backspace to output
                std::cout << "\b \b" << std::flush;

                //remove last character from string
                if ( !line.empty() )
                {
                    line.pop_back();
                }
            }
            break;

        default:

            for ( WORD i = 0; i < ir.Event.KeyEvent.wRepeatCount; i++ )
            {
                //echo input to output
                std::cout << ir.Event.KeyEvent.uChar.AsciiChar << std::flush;

                //add character to input buffer
                line.push_back( ir.Event.KeyEvent.uChar.AsciiChar );
            }
        }       
    }
}

此程序具有以下行为:

5+3
8
10*4
40
5-3
2
20/3
6.66667


Right shift press detected, exiting program!

如您所见,当用户按下右 SHIFT 按钮时,它会立即正确地做出反应。

【讨论】:

    猜你喜欢
    • 2022-01-16
    • 2021-11-20
    • 1970-01-01
    • 2021-03-02
    • 1970-01-01
    • 2022-01-18
    • 2015-12-22
    • 1970-01-01
    • 2019-03-19
    相关资源
    最近更新 更多