C ++ перехватывать Ctrl + C без использования глобальных

Я упростил мой пример для более простого объяснения. Я пишу приложение, которое насчитывает до 100, но в любой момент я позволяю пользователю отменить программу, введя ctrl+c через клавиатуру.

То, что, казалось бы, начиналось как простая программа, быстро усложнилось из-за недостатка знаний о указателях функций. Вот что я пытаюсь сделать:

  1. Захват сигнала SIGINT при нажатии ctrl+c
  2. После захвата вызовите функцию-член, которая закрывает сторонний ресурс.

Суть в том, что в отличие от двух примеров, которые и приводят при захвате SIGINT , мне не разрешается хранить какие-либо глобальные переменные. Идеальный сценарий - это сценарий, в котором все переменные и вызовы функций, связанные с signal() , инкапсулированы в моем классе.

Вот моя измененная попытка, основанная на коде Хайдля и Грижеша :

#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() , закомментировал, но компилятор не рад тому факту, что function<void(int)> C ++ function<void(int)> не эквивалентна языку C void (*)(int) .

Любая критика приветствуется. Я совсем не привязан к тому, что написал, и у меня явно нет глубокого фундаментального понимания того, как использовать указатели на функции-члены.

Всего 1 ответ


Невозможно установить связь между обработчиком сигнала и остальной частью программы, используя локальные переменные. Никакие параметры не передаются в обработчик, кроме повышенного сигнала, и обработчик не возвращает значения.

Слова «глобальные переменные» несколько неоднозначны. Люди иногда имеют в виду разные вещи в зависимости от контекста. Если ваше ограничение применяется только к глобальной области действия, просто используйте volatile sig_atomic_t в некотором пространстве имен. Или используйте статическую переменную-член, если вы так предпочитаете.

Если ваше ограничение относится к статической продолжительности хранения, вы можете вместо этого использовать локальную переменную потока.

Если ваше ограничение распространяется на всю глобальную память, то ваша проблема неразрешима с помощью обработчика сигнала. Вам просто нужна какая-то глобальная переменная.


Если вы можете полагаться на POSIX, а не на стандарт C ++, способ обработки SIGINT без глобальных переменных - убедиться, что он не обработан, и заблокировать поток с помощью sigwait . Если при вызове возвращается SIGINT , остановите программу, в противном случае сделайте то, что вы хотите сделать с перехваченным сигналом.

Конечно, это означает, что блокирующий поток не делает ничего, кроме ожидания сигналов. Вам нужно будет выполнить фактическую работу в других потоках.

Хотя технически глобальная память, вероятно, все еще используется. Использование просто скрыто внутри системной библиотеки.


Кроме того, небезопасно использовать std::cout в обработчике сигнала. Я знаю, что это только пример, но «вызов внешней библиотеки для очистки» очень вероятно, что асинхронный сигнал небезопасен.

Это можно исправить, просто вызвав очистку вне цикла for, а не внутри обработчика.


Основная проблема в том, что я не могу настроить объявление signal() для привязки к моему экземпляру MyClass::sig_handler .

Это потому, что для signal требуется указатель на функцию (типа void(int) ). Нестатические функции-члены не могут указываться указателями функций. Они могут указываться только указателями на функции-члены, которые не принимаются.


Есть идеи?

10000