30

I have written this program that has a main function, inside which, I am creating two sockets, like this:

int sockfd1 = socket(AF_INET, SOCK_STREAM, 0);
int sockfd2 = socket(AF_INET, SOCK_STREAM, 0);

Now I do some stuff with them, and when the user presses Ctrl+C to terminate the process, I want to make sure the sockets close properly, so I do this:

auto sigTermHandler = [&] (int param) { close(sockfd1); close(sockfd2); };
signal(SIGTERM, sigTermHandler);

But this throws the following compilation error when compiled as 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)’

Is it not possible to use lambda this way to handle signals? Please advise.

P.S. I know I could put that in a destructor, if I did proper OOP, but I am curious to see if this works.

R. Martinho Fernandes
  • 228,013
  • 71
  • 433
  • 510
Subhamoy S.
  • 6,566
  • 10
  • 37
  • 53
  • While this would certainly not help you, in case someone else in the future might need to use std::signal but are facing this issue, and can officially use Asio in the codebase, then Asio supports this. – pratikpc Feb 28 '21 at 18:41

3 Answers3

35

A little late but if someone needs such a solution one can use std::function as a wrapper to hold a lambda capable of capturing variables:

#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();
}
Michael Haidl
  • 5,384
  • 25
  • 43
29

You cannot use the capture feature from lambda when calling a simple function pointer. The standard states that a lambda function without a capture is convertible to a function pointer, though:

5.1.2 (6) The closure type for a lambda-expression with no lambda-capture has a public non-virtual non-explicit const conversion function to pointer to function having the same parameter and return types as the closure type’s function call operator. The value returned by this conversion function shall be the address of a function that, when invoked, has the same effect as invoking the closure type’s function call operator.

For instance, this works:

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

But not this:

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

You could actually keep sockfd1 and sockfd2 as global variables and then, you could use them in the lambda function. But that is clearly not a good design. So it is better to use a RAII design. And if the program is terminated the sockets will be closed anyway (as @Dani is pointing out).

betabandido
  • 18,946
  • 11
  • 62
  • 76
0

Sockets will always be closed when a program is closed, no need to worry about it.
If you worry about logical resource handling, put it in destructors, but those won't be called when the user presses CTRL-C

Daniel
  • 30,896
  • 18
  • 85
  • 139
  • That's true, but they will not be closed immediately (up to a few seconds). If you run a program quickly twice in a row, for a socket bound to the specific port, you can get some error about the socket not able to bind. – Sebastian Apr 27 '21 at 08:48