Advertisement
Not a member of Pastebin yet?
Sign Up,
it unlocks many cool features!
- /**********************************************************************************
- Przede wszystkim fajnie byłoby, gdyby nasza aplikacja pozwalała na następujący scenariusz: wstrzymujemy uruchomioną symulację, przełączamy się do interfejsu użytkownika, a w nim dokonujemy np. zapisu/odczytu z pliku, wyświetlenia statystyk czy innej modyfikacji modelu. Po dokonanych operacjach wznawiamy symulację.
- U mnie wygląda to mniej więcej tak:
- ***********************************************************************************/
- void symulacja()
- {
- // przygotowanie do wznowienia
- // ...
- while(!warunek_zakonczenia_symulacji)
- {
- // kolejny krok symulacji
- // ...
- opoznienie();
- }
- // przygotowanie do wstrzymania
- // ...
- }
- int main()
- {
- while(!warunek_zakonczenia_aplikacji)
- {
- polecenie = wczytaj_polecenie();
- if(polecenie == StartSymulacja)
- symulacja();
- else if(polecenie == Koniec)
- warunek_zakonczenia_symulacji = true;
- // else if ...
- else
- wypisz_komunikat_o_nieznanym_poleceniu();
- }
- return 0;
- }
- /**********************************************************************************
- Pojawia się jednak jeden problem: jak asynchronicznie (bez czekania na dane) powiadomić naszą pętlę symulacji, że chcemy z niej wyskoczyć?
- Niestety C++ sam w sobie nie oferuje nic co pozwalałoby na asynchroniczne sprawdzanie stanu wejścia.
- Propozycje:
- -> Pod Linuxem można użyć jakiejś libki do obsługi terminala jak ncurses. Zapewne istnieją tam funkcje, które pozwalają na sprawdzenie stanu klawisza bez czekania. Rozwiązanie rozsądne: ja jednak wolałem zostać przy aplikacji przynajmniej z grubsza strumieniowej i nie wplątywać tam funkcji typowych dla terminala.
- -> Windows oferuje nam GetAsyncKeyState.
- -> Coś mniej typowego, ale i mniej inwazyjnego: obsługa kombinacji CTRL-C.
- ***********************************************************************************/
- #include "ctrlchandler.h"
- void symulacja()
- {
- CtrlCHandler::Set();
- while(!CtrlCHandler::GetState())
- {
- // kolejny krok symulacji
- // ...
- opoznienie();
- }
- CtrlCHandler::Release();
- /*
- ISTOTNA UWAGA!
- Jeśli w naszej pętli pojawia sie jakiś return czy rzucany jest wyjątek: trzeba uprzednio koniecznie wykonac CtrlCHandler::Release()
- W przypadku gdy nasza symulacja rzuca wyjątkami: można równie dobrze wykonywać "na zapas" tą funkcję w catch'u.
- */
- }
- int main()
- {
- // ...
- return 0;
- }
- /**********************************************************************************
- Jak to działa? Domyślnie CTRL-C przerywa działanie naszej aplikacji (pod Linuxem rzucając sygnałem SIGINT). My możemy jednak na czas symulacji podpiąć własną procedurę obsługi, która przy wciśnięciu tej kombinacji ustawi nam odpowiednią flagę, która zostanie następnie odczytania przez GetState.
- Napisany przeze mnie handler zawiera zarówno wersję dla WinAPI, jak i pod Linux.
- ***********************************************************************************/
- /* ctrlchandler.h *******************************************/
- #ifndef _CTRL_C_HANDLER
- #define _CTRL_C_HANDLER
- //! Niestandardowa procedura obslugi kombinacji CTRL-C
- namespace CtrlCHandler
- {
- //! Ustawia niestandardowy handler
- extern void Set();
- //! Zwalnia niestandardowy handler (przywraca domyslny)
- extern void Release();
- //! Zwraca aktualny i zeruje stan wywolania
- extern bool GetState();
- }
- #endif
- /* ctrlchandler.cpp *****************************************/
- #ifdef __linux__
- #include <csignal>
- #include <unistd.h>
- #elif _WIN32
- #include <Windows.h>
- #include <atomic>
- #else
- #error "OS not supported: turn off CTRL-C handling"
- #endif
- namespace CtrlCHandler
- {
- #ifdef __linux__
- volatile sig_atomic_t stop = 0;
- void handler(int s);
- #elif _WIN32
- std::atomic<int> stop = 0;
- BOOL __stdcall handler(DWORD dwCtrlType);
- #endif
- void Set();
- void Release();
- bool GetState();
- }
- #ifdef __linux__
- void CtrlCHandler::handler(int s)
- {
- CtrlCHandler::stop = 1;
- }
- #elif _WIN32
- BOOL __stdcall CtrlCHandler::handler(DWORD dwCtrlType)
- {
- if (dwCtrlType == CTRL_C_EVENT)
- {
- CtrlCHandler::stop = 1;
- return TRUE;
- }
- else
- return FALSE;
- }
- #endif
- void CtrlCHandler::Set()
- {
- stop = 0;
- #ifdef __linux__
- signal(SIGINT, CtrlCHandler::handler);
- #elif _WIN32
- SetConsoleCtrlHandler((PHANDLER_ROUTINE)CtrlCHandler::handler, TRUE);
- #endif
- };
- void CtrlCHandler::Release()
- {
- #ifdef __linux__
- signal(SIGINT, SIG_DFL);
- #elif _WIN32
- SetConsoleCtrlHandler((PHANDLER_ROUTINE)CtrlCHandler::handler, FALSE);
- #endif
- }
- bool CtrlCHandler::GetState()
- {
- bool result = (stop == 1);
- if (result)
- stop = 0;
- return result;
- }
Advertisement
Add Comment
Please, Sign In to add comment
Advertisement