Использование 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