How to map 'Ctrl + c' with C++

Sometimes we need 'to return something to the system' when we finish a program, but it may be that the user 'killed the process' before the end of it without being expected.


How to map 'Ctrl + c' with C++


Sometimes we need to return something to the system when we finish a program, but it may be that the user killed the process before the end of it without being expected.

It happens a lot in programs that have a loop while with tasks to be executed to the expected end!

Suppose you have this code that issues a warning at the beginning of the program and after 5 seconds the program ends and issues another warning:

#include <iostream>
#include <memory>
#include <chrono>
#include <thread>

void msgOut(){
   std::cout << "The 5 second count has finished." << '\n';
}

class CountTime {

   void m_start_count(){
     std::this_thread::sleep_for(std::chrono::seconds(5));
   }

   public:
     CountTime(){
       std::cout << "The 5 second countdown has started!" << '\n';
       this->m_start_count();
     }

     ~CountTime(){
       msgOut();
     }
};

int main(){
   auto ct = std::make_unique<CountTime>();
   return 0;
}

After compiling and running, after 5 seconds these two messages appeared in the output:

The 5 second countdown has started!
The 5 second count has ended.

Now suppose that before the end of 5 seconds, you press Ctrl + c, the second message will not appear and the output will be like this:

The 5 second countdown has started!
^C

In other words, if your program has a function to be executed whenever the program ends, then it will generate a silent bug.

To solve this, we can map the signal sent and execute a certain task even if the program is interrupted before its normally expected end.


Using std::signal

First let’s include the header:

#include <csignal>

Create a callback function that will handle the signal, outside the execution of our class:

void signal_handler(int signal) {
   if (signal == SIGINT) {
     msgOut();
     std::exit(EXIT_SUCCESS);
   }
}

And start it in the constructor, or before the start of a supposed loop that we will execute!

std::signal(SIGINT, signal_handler);

The final code will be:

#include <iostream>
#include <memory>
#include <chrono>
#include <thread>
#include <csignal> // include

void msgOut(){
   std::cout << "The 5 second count has finished." << '\n';
}

// Our function for handling the signal
void signal_handler(int signal) {
   if (signal == SIGINT) {
     msgOut();
     std::exit(EXIT_SUCCESS);
   }
}

class CountTime {

   void m_start_count(){
     std::this_thread::sleep_for(std::chrono::seconds(5));
   }

   public:
     CountTime(){
       std::signal(SIGINT, signal_handler); // Configuring signal handling
       std::cout << "The 5 second countdown has started!" << '\n';
       this->m_start_count();
     }

     ~CountTime(){
       msgOut();
     }
};

int main(){
   auto ct = std::make_unique<CountTime>();
   return 0;
}

After compiling and running, after pressing Ctrl + c, the output will now be:

The 5 second countdown has started!
^CThe 5 second count has finished.

Note that after Ctrl + c(^C) the message appeared normally!


Another example without Object Orientation and with while

#include <iostream>
#include <csignal>

void signal_handler(int signal) {
   if (signal == SIGINT) {
     std::cout << "You pressed: Ctrl + C\n";
     std::exit(EXIT_SUCCESS);
   }
}

auto main() -> int {
   std::signal(SIGINT, signal_handler);
   while (true){}
}

Output after Ctrl + c:

^CDou pressed: Ctrl + C

I hope it helped, for more information visit documentation about std::signal.


cpp


Share


YouTube channel

Subscribe


Marcos Oliveira

Marcos Oliveira

Software developer
https://github.com/terroo