24

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

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)’

不能以这种方式使用 lambda 来处理信号吗?请指教。

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

4

3 回答 3

27

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

5.1.2 (6) 没有 lambda-capture 的 lambda 表达式的闭包类型有一个公共的非虚非显式 const 转换函数,指向具有与闭包类型的函数调用运算符相同的参数和返回类型的函数的指针。这个转换函数的返回值应该是一个函数的地址,当被调用时,它与调用闭包类型的函数调用运算符具有相同的效果。

例如,这有效:

signal(SIGTERM, [](int signum) { /* ... */ });

但不是这个:

signal(SIGTERM, [foo](int signum) { /* use foo here */ });

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

于 2012-07-13T10:15:36.460 回答
25

有点晚了,但是如果有人需要这样的解决方案,可以将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();
}
于 2018-01-09T08:39:04.883 回答
0

当程序关闭时,套接字将始终关闭,无需担心。
如果您担心逻辑资源处理,请将其放入析构函数中,但当用户按下 CTRL-C 时不会调用这些析构函数

于 2012-07-13T10:18:58.920 回答