The following code is a signal implementation copied from APUE with a little modification
namespace
{
using signal_handler = void (*)(int);
signal_handler signal(sigset_t sig, signal_handler);
}
Signal::signal_handler Signal::signal(sigset_t sig, void (*handler)(int))
{
struct sigaction newAction, oldAction;
sigemptyset(&newAction.sa_mask);
newAction.sa_flags = 0;
newAction.sa_handler = handler;
if (sig == SIGALRM)
{
#ifdef SA_INTERRUPT
newAction.sa_flags |= SA_INTERRUPT;
#endif
}
else
{
newAction.sa_flags |= SA_RESTART;
}
if (sigaction(sig, &newAction, &oldAction) < 0)
throw std::runtime_error("signal error: cannot set a new signal handler.")
return oldAction.sa_handler;
}
The above code works fine during my test, but I wanted to make it more like a C++ code, so I changed signal_handler alias to
using signal_handler = std::function<void (int)>;
and also I use
newAction.sa_handler = handler.target<void (int)>();
to replace
newAction.sa_handler = handler;
and now there is a problem. I find newAction.sa_handler is still NULL after
newAction.sa_handler = handler.target<void (int)>();
but I don't know why. Anyone can help me explain this? thanks. Here is my test code:
void usr1_handler(int sig)
{
std::cout << "SIGUSR1 happens" << std::endl;
}
void Signal::signal_test()
{
try
{
Signal::signal(SIGUSR1, usr1_handler);
}
catch (std::runtime_error &err)
{
std::cout << err.what();
return;
}
raise(SIGUSR1);
}
Even when using the original code when I run it in Xcode, there is no output. Instead, I run the executable file manually, I can see SIGUSR1 happens in the terminal. Why? How can I see the output using Xcode?
The direct answer is that target()
is very picky - you must name the type of the target exactly to get a pointer to it, otherwise you get a null pointer. When you set your signal to usr1_handler
, that is a pointer to a function (not a function) - its type is void(*)(int)
, not void(int)
. So you're simply giving the wrong type to target()
. If you change:
handler.target<void (int)>();
to
handler.target<void(*)(int)>();
that would give you the correct target.
But note what target()
actually returns:
template< class T >
T* target();
It returns a pointer to the provided type - in this case that would be a void(**)(int)
. You'd need to dereference that before doing further assignment. Something like:
void(**p)(int) = handler.target<void(*)(int)>();
if (!p) {
// some error handling
}
newAction.sa_handler = *p;
Demo.
However, the real answer is that this makes little sense to do. std::function<Sig>
is a type erased callable for the given Sig
- it can be a pointer to a function, a pointer to a member function, or even a wrapped function object of arbitrary size. It is a very generic solution. But sigaction
doesn't accept just any kind of generic callable - it accepts specifically a void(*)(int)
.
By creating a signature of:
std::function<void(int)> signal(sigset_t sig, std::function<void(int)> );
you are creating the illusion that you are allowing any callable! So, I might try to pass something like:
struct X {
void handler(int ) { ... }
};
X x;
signal(SIGUSR1, [&x](int s){ x.handler(s); });
That's allowed by your signature - I'm providing a callable that takes an int
. But that callable isn't convertible to a function pointer, so it's not something that you can pass into sigaction()
, so this is just erroneous code that can never work - this is a guaranteed runtime failure.
Even worse, I might pass something that is convertible to a function pointer, but may not know that that's what you need, so I give you the wrong thing:
// this will not work, since it's not a function pointer
signal(SIGUSR1, [](int s){ std::cout << s; });
// but this would have, if only I knew I had to do it
signal(SIGUSR1, +[](int s){ std::cout << s; });
Since sigaction()
limits you to just function pointers, you should limit your interface to it to just function pointers. Strongly prefer what you had before. Use the type system to catch errors - only use type erasure when it makes sense.
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With