c ++无需使用全局变量即可捕获ctrl + c

时间:2019-05-14 23:53:30

标签: c++ c lambda function-pointers sigint

为了简化说明,我简化了示例。我正在编写一个数量为100的应用程序,但是在任何给定时间,我都允许用户通过键盘输入ctrl+c来取消该程序。

由于我对函数指针的缺乏了解,看似简单程序的东西很快变得复杂起来。这就是我正在尝试做的事情:

  1. 按下ctrl+c时捕获SIGINT信号。
  2. 一旦捕获,请调用一个用于关闭第三方资源的成员函数。

要注意的是,与Michael HaidlGrijesh Chauhan在捕获SIGINT时给出的两个示例不同,我不允许存储任何全局变量。理想的情况是将与signal()相关的所有变量和函数调用封装在我的一类中。

这是我根据 Haidl Grijesh的代码进行的修改尝试:

#include <thread>
#include <chrono>
#include <functional>
#include <iostream>
#include <signal.h>

class MyClass {
    public:
        volatile sig_atomic_t cancel = 0;
        void sig_handler(int signal) { 
            cancel = true; 
            this->libCancel();
        }   
        void libCancel() { std::cout << "Cancel and cleanup" << std::endl; }
};

int main(int argc, char *argv[]) {

  MyClass mc; 
  //using std::placeholders::_1;
  //std::function<void(int)> handler = std::bind(&MyClass::sig_handler, mc, _1);
  //signal(SIGINT, handler);
  signal(SIGINT, &mc.sig_handler); // **compiler error** 

  for (int i = 0; !mc.cancel && i < 100; ++i)
  {
      std::cout << i << std::endl;
      std::this_thread::sleep_for(std::chrono::seconds(1));
  }
  return 0;
}

如您所见,我希望代码简单地计数到100,如果一切顺利,则退出。但是,如果用户调用ctrl+c,则该类应处理SIGINT,调用外部库进行清理,然后for循环将退出。

主要问题是我似乎无法设置signal()声明来绑定到我的MyClass::sig_handler实例。我甚至尝试将成员函数强制转换为std::function以供signal()使用,但已被注释掉,但是编译器对于C ++ function<void(int)>不等同于C的事实并不满意。 lang void (*)(int)

欢迎任何批评。我完全不依赖于所写的内容,而且我对如何将函数指针与成员函数一起使用也没有很深入的了解。

1 个答案:

答案 0 :(得分:4)

使用局部变量无法在信号处理程序和程序的其余部分之间进行通信。除了引发信号外,没有参数传递到处理程序中,并且处理程序不返回任何值。

“全局变量”一词有些含糊。人们有时根据上下文的含义不同。如果您的限制仅适用于全局范围,则只需在某些命名空间中使用volatile sig_atomic_t。或者,如果愿意,可以使用静态成员变量。

如果您的限制适用于静态存储持续时间,则可以改用线程局部变量。

如果您的限制适用于所有全局内存,那么使用信号处理程序将无法解决您的问题。您只需要某种全局变量即可。


如果您可以依靠POSIX而不是C ++标准,则不使用全局变量来处理SIGINT的一种方法是确保它不是 处理的,并使用{{1 }}。如果调用返回sigwait,则停止程序,否则对捕获的信号执行您想做的事情。

当然,这意味着阻塞线程除了等待信号外不执行其他任何操作。您需要在其他线程中进行实际工作。

尽管从技术上讲,全局内存可能仍在使用。使用只是隐藏在系统库中。


此外,在信号处理程序中使用SIGINT是不安全的。我知道这只是一个例子,但是“调用外部库进行清理” 很可能也是异步信号不安全的原因。

只需在for循环外部而不是在处理程序内部调用清除即可解决此问题。


  

主要问题是我似乎无法设置std::cout声明来绑定到我的signal()实例。

这是因为MyClass::sig_handler需要一个函数指针(类型为signal)。非静态成员函数不能由函数指针指向。它们只能由成员函数指针指向,void(int)不接受。