Advertisement
Guest User

Model symulacji i obsługa CTRL-C

a guest
Apr 24th, 2014
199
0
Never
Not a member of Pastebin yet? Sign Up, it unlocks many cool features!
C++ 4.66 KB | None | 0 0
  1. /**********************************************************************************
  2. 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ę.
  3. U mnie wygląda to mniej więcej tak:
  4. ***********************************************************************************/
  5.  
  6. void symulacja()
  7. {
  8.     // przygotowanie do wznowienia
  9.     // ...
  10.     while(!warunek_zakonczenia_symulacji)
  11.     {
  12.         // kolejny krok symulacji
  13.         // ...
  14.         opoznienie();
  15.     }
  16.     // przygotowanie do wstrzymania
  17.     // ...
  18. }
  19.  
  20. int main()
  21. {
  22.     while(!warunek_zakonczenia_aplikacji)
  23.     {
  24.         polecenie = wczytaj_polecenie();
  25.         if(polecenie == StartSymulacja)
  26.             symulacja();
  27.         else if(polecenie == Koniec)
  28.             warunek_zakonczenia_symulacji = true;
  29.         // else if ...
  30.         else
  31.             wypisz_komunikat_o_nieznanym_poleceniu();
  32.     }
  33.     return 0;
  34. }
  35.  
  36. /**********************************************************************************
  37. Pojawia się jednak jeden problem: jak asynchronicznie (bez czekania na dane) powiadomić naszą pętlę symulacji, że chcemy z niej wyskoczyć?
  38. Niestety C++ sam w sobie nie oferuje nic co pozwalałoby na asynchroniczne sprawdzanie stanu wejścia.
  39. Propozycje:
  40. -> 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.
  41. -> Windows oferuje nam GetAsyncKeyState.
  42. -> Coś mniej typowego, ale i mniej inwazyjnego: obsługa kombinacji CTRL-C.
  43. ***********************************************************************************/
  44.  
  45. #include "ctrlchandler.h"
  46.  
  47. void symulacja()
  48. {
  49.     CtrlCHandler::Set();
  50.     while(!CtrlCHandler::GetState())
  51.     {
  52.         // kolejny krok symulacji
  53.         // ...
  54.         opoznienie();
  55.     }
  56.     CtrlCHandler::Release();
  57.     /*
  58.     ISTOTNA UWAGA!
  59.     Jeśli w naszej pętli pojawia sie jakiś return czy rzucany jest wyjątek: trzeba uprzednio koniecznie wykonac CtrlCHandler::Release()
  60.     W przypadku gdy nasza symulacja rzuca wyjątkami: można równie dobrze wykonywać "na zapas" tą funkcję w catch'u.
  61.     */
  62. }
  63.  
  64. int main()
  65. {
  66.     // ...
  67.     return 0;
  68. }
  69.  
  70. /**********************************************************************************
  71. 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.
  72. Napisany przeze mnie handler zawiera zarówno wersję dla WinAPI, jak i pod Linux.
  73. ***********************************************************************************/
  74.  
  75. /* ctrlchandler.h *******************************************/
  76.  
  77. #ifndef _CTRL_C_HANDLER
  78. #define _CTRL_C_HANDLER
  79.  
  80. //! Niestandardowa procedura obslugi kombinacji CTRL-C
  81. namespace CtrlCHandler
  82. {
  83.     //! Ustawia niestandardowy handler
  84.     extern void Set();
  85.     //! Zwalnia niestandardowy handler (przywraca domyslny)
  86.     extern void Release();
  87.     //! Zwraca aktualny i zeruje stan wywolania
  88.     extern bool GetState();
  89. }
  90.  
  91. #endif
  92.  
  93. /* ctrlchandler.cpp *****************************************/
  94.  
  95. #ifdef __linux__
  96. #include <csignal>
  97. #include <unistd.h>
  98. #elif _WIN32
  99. #include <Windows.h>
  100. #include <atomic>
  101. #else
  102. #error "OS not supported: turn off CTRL-C handling"
  103. #endif
  104.  
  105. namespace CtrlCHandler
  106. {
  107. #ifdef __linux__
  108.     volatile sig_atomic_t stop = 0;
  109.     void handler(int s);
  110. #elif _WIN32
  111.     std::atomic<int> stop = 0;
  112.     BOOL __stdcall handler(DWORD dwCtrlType);
  113. #endif
  114.     void Set();
  115.     void Release();
  116.     bool GetState();
  117. }
  118.  
  119. #ifdef __linux__
  120.  
  121. void CtrlCHandler::handler(int s)
  122. {
  123.     CtrlCHandler::stop = 1;
  124. }
  125.  
  126. #elif _WIN32
  127.  
  128. BOOL __stdcall CtrlCHandler::handler(DWORD dwCtrlType)
  129. {
  130.     if (dwCtrlType == CTRL_C_EVENT)
  131.     {
  132.         CtrlCHandler::stop = 1;
  133.         return TRUE;
  134.     }
  135.     else
  136.         return FALSE;
  137. }
  138.  
  139. #endif
  140.  
  141. void CtrlCHandler::Set()
  142. {
  143.     stop = 0;
  144. #ifdef __linux__
  145.     signal(SIGINT, CtrlCHandler::handler);
  146. #elif _WIN32
  147.     SetConsoleCtrlHandler((PHANDLER_ROUTINE)CtrlCHandler::handler, TRUE);
  148. #endif
  149. };
  150.  
  151. void CtrlCHandler::Release()
  152. {
  153. #ifdef __linux__
  154.     signal(SIGINT, SIG_DFL);
  155. #elif _WIN32
  156.     SetConsoleCtrlHandler((PHANDLER_ROUTINE)CtrlCHandler::handler, FALSE);
  157. #endif
  158. }
  159.  
  160. bool CtrlCHandler::GetState()
  161. {
  162.     bool result = (stop == 1);
  163.     if (result)
  164.         stop = 0;
  165.     return result;
  166. }
Advertisement
Add Comment
Please, Sign In to add comment
Advertisement