A method for handling SIGWINCH in C++
Programs running in a terminal are notified of terminal resize events with the SIGWINCH signal. To catch and process such a signal, we use the signal(3) command, which accepts a function to be called when the signal is caught by the program:
void (*signal(int sig, void (*func)(int)))(int);As noted here, the C++ standard (section 18.7/5) states that:
"All signal handlers shall have C linkage. A POF [Plain Old Function] that could be used as a signal handler in a conforming C program does not produce undefined behavior when used as a signal handler in a C++ program. The behavior of any other function used as a signal handler in a C++ program is implementation defined... In particular, a signal handler using exception handling is very likely to have problems."
Furthermore, section 1.9/9 states that:
"When the processing of the abstract machine is interrupted by a signal, the values of objects with type other than volatile sig_atomic_t are unspecified, and the value of any object not of volatile sig_atomic_t that is modified by the handler becomes undefined."
These two requirements severely limit our options for handling signals with modern C++ design techniques.
One solution I have identified involves using a single sig_atomic_t variable to be set by our POF signal handler, and to be polled periodically for change by our application's main loop. Here is an example static class (and associated POF handler) that can be polled for terminal size change:
extern "C"
{
volatile sig_atomic_t sigwinch_flag = 0;
void handle_sigwinch(int signal)
{
sigwinch_flag = 1;
}
} // extern "C"
class sigwinch_handler
{
public:
static bool caught_event()
{
return (sigwinch_flag != 0);
}
static bool reset()
{
sigwinch_flag = 0;
}
static void attach()
{
if (signal(SIGWINCH, &handle_sigwinch) == SIG_ERR)
throw new exception("Could not attach signal handler: " + string(strerror(errno)));
}
static void detach()
{
if (signal(SIGWINCH, SIG_DFL) == SIG_ERR)
throw new exception("Could not detach signal handler: " + string(strerror(errno)));
}
}; // class sigwinch_handlerExample usage:
// Main application loop.
void run()
{
// Attach SIGWINCH handler.
sigwinch_handler::attach();
for (;;)
{
// Poll keyboard and mouse events etc.
if (sigwinch_handler::caught_event())
{
// Handle new terminal size.
sigwinch_handler::reset();
}
}
// Detach SIGWINCH handler.
sigwinch_handler::detach();
}You can read more about signal handling in C++ here.
No comments:
Post a Comment