Я упростил мой пример для более простого объяснения. Я пишу приложение, которое насчитывает до 100, но в любой момент я позволяю пользователю отменить программу, введя ctrl+c
через клавиатуру.
То, что, казалось бы, начиналось как простая программа, быстро усложнилось из-за недостатка знаний о указателях функций. Вот что я пытаюсь сделать:
ctrl+c
Суть в том, что в отличие от двух примеров, которые и приводят при захвате 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)
). Нестатические функции-члены не могут указываться указателями функций. Они могут указываться только указателями на функции-члены, которые не принимаются.