getch() nachbauen



  • Hallo,

    ich würde gerne mit C++-Standardmitteln getch() und _kbhit() nachbauen, da ich sowohl Linux als auch Windows nutze und somit plattformunabhängig bleiben will.
    Folgende Idee für _kbhit(): die Größe des Inhalts des Buffers überprüfen in einer while()-Schleife, bzw. einen Buffer setzen und dessen Inhalt überprüfen:

    #include <iostream>
    #include <thread>
    #include <chrono>
    
    int main(int argc, char* argv[])
    {
            std::cout << "0: false; 1: true\n";
    
            char buffer[16];
            auto a = std::cin.rdbuf()->pubsetbuf(buffer, sizeof(buffer));
            buffer[0] = '\\';
    //      while(std::cin.rdbuf()->in_avail() == 0)
    //      while(std::cin.peek() == '\n')
            while(buffer[0] == '\\')
            {
                    std::this_thread::sleep_for(std::chrono::milliseconds(250));
            }
    
            std::cout << "Triggered!";
    
            return 0;
    }
    

    Irgendwie verändert sich der Buffer aber nicht. Verwendet std::cin diesen gar nicht?

    So ähnlich würde es dann natürlich auch für getch() aussehen, nur, dass ich dann eben buffer[0] zurückgebe und ein '\b'-Zeichen ausgebe + Leerstelle.

    Nächster Ansatz:

    #include <iostream>
    
    int main(int argc, char* argv[])
    {
            auto a = std::cin.rdbuf();
            auto b = std::cout.rdbuf();
            char x;
            a->pubsetbuf(&x, 0);
    
            while(std::cin.peek() == EOF)
            {
    
            }
            std::cin.putback('\n');
    
        char c = std::cin.get();
    
            return 0;
    }
    

    Die Idee hier ist, dass ich den Buffer ausschalte und mit std::cin.peek() warte, bis im Buffer etwas ist. Mit std::cin.get() soll das dann sofort gelesen werden, da danach im Stream ja noch ein <Enter> ist.

    Beides funktioniert irgendwie nicht. Hat jemand eine Erklärung, warum? Und wie man das zum Laufen bringen kann, bzw. Alternativen?



  • Die erste Variante funktioniert nicht, da die Daten ja nicht "von selbst" (=ohne dass du ne IO Funktion aufrufst) im Puffer landen. Und wenn du ne IO Funktion aufrufst, dann blockiert die natürlich wieder.

    Die 2. Variante sollte funktionieren, allerdings nur insofern dass peek() auf stdin so lange blockiert bis ein Zeichen verfügbar ist. (Oder der Stream geschlossen wurde, z.B. wenn stdin gepiped wird und das Daten-generierende Programm terminiert bzw. das "Input-File" EOF ist). Das verfügbare Zeichen liest es dann von stdin und steckt es in den Puffer von std::cin , so dass es danach normal aus std::cin "gelesen" werden kann.
    Wenn du während keine Taste gedrückt wird noch 'was anderes machen möchtest, müsstest du das Lesen vom std::istream (bzw. das Peeken) in einen eigenen Thread verlagern.
    Ist allerdings auch nicht ganz einfach.

    Die einfachere Lösung ist also vermutlich ein "ifdef windows" zu verwenden, und die nötigen 2-3 Zeilen Code dann 2x zu schreiben: 1x für POSIX Systeme (Linux, BSD, OS X, ...) und 1x für Windows.
    (Hab' jetzt nicht nachgesehen ob es da speziell im POSIX Standard 'was gibt, aber ich würde mal annehmen dass irgenbwo bei POSIX oder Single UNIX Specification bzw. dem Open Group Zeugs was dabei ist mit dem es geht.)


Anmelden zum Antworten