Использование Auto и Lambda для обработки сигнала?
Я написал эту программу, которая имеет главную функцию, внутри которой я создаю два сокета, например:
int sockfd1 = socket(AF_INET, SOCK_STREAM, 0);
int sockfd2 = socket(AF_INET, SOCK_STREAM, 0);
Теперь я делаю с ними что-то, и когда пользователь нажимает Ctrl + C, чтобы завершить процесс, я хочу убедиться, что сокеты закрыты правильно, поэтому я делаю это:
auto sigTermHandler = [&] (int param) { close(sockfd1); close(sockfd2); };
signal(SIGTERM, sigTermHandler);
Но это порождает следующую ошибку компиляции при компиляции как g++ -std=gnu++0x <filename>.cpp
:
error: cannot convert ‘main(int, char**)::<lambda(int)>’ to ‘__sighandler_t {aka void (*)(int)}’ for argument ‘2’ to ‘void (* signal(int, __sighandler_t))(int)’
Невозможно ли использовать лямбда таким образом, чтобы обрабатывать сигналы? Просьба сообщить.
P.S. Я знаю, что могу поместить это в деструктор, если бы я сделал правильный ООП, но мне любопытно посмотреть, работает ли это.
Ответы
Ответ 1
Вы не можете использовать функцию захвата из лямбда при вызове простого указателя функции. В стандарте указано, что функция лямбда без захвата конвертируется в указатель на функцию:
5.1.2 (6) Тип замыкания для лямбда-выражения без лямбда-захвата имеет общедоступную не виртуальную неявную константу функция преобразования для указателя на функцию, имеющую тот же параметр и возвращаемые типы, что и типы закрытия оператор вызова функции. Значение, возвращаемое этой функцией преобразования, должно быть адресом функции что при вызове имеет тот же эффект, что и при вызове оператора вызова функции замыкания.
Например, это работает:
signal(SIGTERM, [](int signum) { /* ... */ });
Но не это:
signal(SIGTERM, [foo](int signum) { /* use foo here */ });
Фактически вы могли бы сохранить sockfd1
и sockfd2
в качестве глобальных переменных, а затем вы могли бы использовать их в функции лямбда. Но это явно не очень хороший дизайн. Поэтому лучше использовать дизайн RAII. И если программа завершена, сокеты все равно будут закрыты (как указывает @Dani).
Ответ 2
Немного поздно, но если кому-то нужно такое решение, можно использовать std::function
в качестве обертки для хранения лямбда, способной захватывать переменные:
#include <functional>
#include <iostream>
namespace {
std::function<void(int)> shutdown_handler;
void signal_handler(int signal) { shutdown_handler(signal); }
} // namespace
int main(int argc, char *argv[]) {
std::signal(SIGINT, signal_handler);
MyTCPServer server;
shutdown_handler = [&](int signal) {
std::cout << "Server shutdown...\n";
server.shutdown();
};
server.do_work_for_ever();
}
Ответ 3
Сокеты всегда будут закрыты, когда программа закрыта, не нужно беспокоиться об этом.
Если вы беспокоитесь об обработке логических ресурсов, поместите его в деструкторы, но они не будут вызываться, когда пользователь нажимает CTRL-C