使用Auto和Lambda处理Signal?

Sub*_* S. 15 c++ lambda posix signals c++11

我编写了这个程序,它有一个main函数,在其中,我创建了两个套接字,如下所示:

int sockfd1 = socket(AF_INET, SOCK_STREAM, 0);
int sockfd2 = socket(AF_INET, SOCK_STREAM, 0);
Run Code Online (Sandbox Code Playgroud)

现在我用它们做一些事情,当用户按下Ctrl + C终止进程时,我想确保套接字正确关闭,所以我这样做:

auto sigTermHandler = [&] (int param) { close(sockfd1); close(sockfd2); };
signal(SIGTERM, sigTermHandler);
Run Code Online (Sandbox Code Playgroud)

但是这会在编译时抛出以下编译错误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)’
Run Code Online (Sandbox Code Playgroud)

是不是可以用这种方式处理信号?请指教.

PS我知道我可以把它放在析构函数中,如果我做了适当的OOP,但我很想知道这是否有效.

bet*_*ido 17

调用简单函数指针时,不能使用lambda的捕获功能.该标准声明没有捕获的lambda函数可以转换为函数指针,但是:

5.1.2(6)没有lambda-capture的lambda表达式的闭包类型有一个公共的非虚拟非显式const转换函数,用于指向具有与闭包类型的函数调用操作符相同的参数和返回类型的函数.此转换函数返回的值应为函数的地址,该函数在调用时与调用闭包类型的函数调用运算符具有相同的效果.

例如,这有效:

signal(SIGTERM, [](int signum) { /* ... */ });
Run Code Online (Sandbox Code Playgroud)

但不是这个:

signal(SIGTERM, [foo](int signum) { /* use foo here */ });
Run Code Online (Sandbox Code Playgroud)

你实际上可以保持sockfd1sockfd2作为全局变量然后,你可以在lambda函数中使用它们.但这显然不是一个好的设计.所以最好使用RAII设计.如果程序终止,套接字无论如何都会被关闭(正如@Dani指出的那样).


Mic*_*idl 13

有点晚但如果有人需要这样的解决方案,可以使用它std::function作为包装器来保存能够捕获变量的lambda:

#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();
}
Run Code Online (Sandbox Code Playgroud)

  • 如果在将 lambda 分配给 `shutdown_handler` 之前发生了 SIGINT(Ctrl+C),这将终止程序并显示一个类型为 `bad_function_call` 的未捕获异常。 (2认同)