c++windowsmacosbuffercin

How to flush cin including all occurrences of newline


My app first takes some input, then it 'thinks' showing a progress bar, then throws an exit code and waits with getchar() for ENTER to be clicked before exiting.

The problem is that if I click ENTER during the 'thinking' process, it will affect the getchar(), causing it to exit immediately after throwing the exit code, without waiting for a new ENTER. I only found methods of clearing the cin buffer until \n what doesn't work in my case - I need to somehow clear the whole buffer just before calling getchar() - I think.

My app is meant for both Windows and MacOS, but in different #ifdefs, so both cases need solving.

The code is similar to this:

int main()
{
    string name;
    getline(cin, name);
    //Think and show progres bar
    cout << "Ended with exit code 1";
    getchar();
    return 1;
}

Solution

  • Here you go.

    On Windows, you can use _kbhit/_getch from conio.h.

    On POSIX-compliant systems, such as MacOS, you can use termios.h to reproduce those functions.

    // You didn't include those three lines in your sample code, but they're implied, so here they are.
    #include <iostream>
    #include <string>
    use namespace std;
    
    // kbhit/getch compatibility
    
    #if defined(_WIN32) || defined(WIN32)
    
    #include <conio.h>
    
    #define kbhit _kbhit
    #define getch _getch
    
    #elif defined (__unix__) || (defined (__APPLE__) && defined (__MACH__)) // Should work for any POSIX-compliant system, including MacOS
    
    #include <sys/ioctl.h>
    #include <sys/select.h>
    #include <termios.h>
    #include <stdarg.h>
    #include <unistd.h>
    
    static int peek = -1;
    inline int kbhit() {
        // Initial variables
        char ch;
        int nread;
        struct termios origTerm, newTerm;
    
        // Check if a character hasn't been buffered yet
        if (peek != -1)
            return 1;
    
        // Preparing the terminal
        tcgetattr(0, &origTerm);
        newTerm = origTerm;
        newTerm.c_lflag &= ~ICANON;
        newTerm.c_lflag &= ~ECHO;
        newTerm.c_lflag &= ~ISIG;
        newTerm.c_cc[VMIN] = 1;
        newTerm.c_cc[VTIME] = 0;
        tcsetattr(0, TCSANOW, &newTerm);
    
        // Read potential keyboard input
        newTerm.c_cc[VMIN] = 0;
        tcsetattr(0, TCSANOW, &newTerm);
        nread = read(0, &ch, 1);
        newTerm.c_cc[VMIN] = 1;
        tcsetattr(0, TCSANOW, &newTerm);
    
        // Restore the terminal
        tcsetattr(0, TCSANOW, &origTerm);
    
        // Check if something was read
        if (nread == 1) {
            peek = ch;
            return 1;
        }
        return 0;
    }
    
    inline char getch() {
        char ch = peek;
        peek = -1;
        return ch;
    }
    
    #else
    
    #error kbhit and getch are not supported.
    
    #endif
    
    // Your code, with a new line to clear the keyboard buffer
    
    int main()
    {
        string name;
        getline(cin, name);
    
        //Think and show progress bar
    
        while (kbhit()) getch(); // Clear keyboard buffer
    
        cout << "Ended with exit code 1";
        getchar();
        return 1;
    }
    

    References: