Catching Signals: Use a Member Function as Signal Handler

Is it possible to use signal inside a C++ class?

The second parameter of signal should be a pointer to a function accepting an int and returning void. What you're passing to signal is a pointer to a member function accepting an int and returning void (its type being void (myClass::*)(int)). I can see three possibilities to overcome this issue:

1 - Your method myHandler can be static: this is great, make it static

class myClass 
{
public:
void myFunction ()
{
signal(SIGIO, myClass::myHandler);
}

static void myHandler (int signum)
{
// handling code
}
};

2 - Your method shouldn't be static: if you're planning to use signal with only one instance, you can create a private static object, and write a static method that simply call the method on this object. Something along the lines of

class myClass 
{
public:
void myFunction ()
{
signal(SIGIO, myClass::static_myHandler);
}

void myHandler (int signum)
{
// handling code
}

static void static_myHandler(int signum)
{
instance.myHandler(signum);
}

private:
static myClass instance;
};

3 - However, if you're planning on using the signal with multiple instances, things will get more complicated. Perhaps a solution would be to store each instance you want to manipulate in a static vector, and invoking the method on each of these :

class myClass
{
public:
void myFunction () // registers a handler
{
instances.push_back(this);
}

void myHandler (int signum)
{
// handling code
}

static void callHandlers (int signum) // calls the handlers
{
std::for_each(instances.begin(),
instances.end(),
std::bind2nd(std::mem_fun(&myClass::myHandler), signum));
}
private:
static std::vector<myClass *> instances;
};

and somewhere, do a single call to

signal(SIGIO, myClass::callHandlers);

But I think that if you end up using the last solution, you should probably think about changing your handling design :-)!

Catching signals: Use a member function as signal handler

  • What do I need to change to make this work? I think f is like void f(int), like the functions the signal handler gets in some examples.

The compiler complains about the type, therefore you need to pass a function pointer, not an object of type boost::function<void(int)>. Creating a global variable of this type, and adding a function which calls this object would work :

boost::function<void(int)> myCb;
void CallCb( int value )
{
myCb(value);
}

int main(int argc, char** argv)
{
Foo foo;
struct sigaction sigIntHandler;

myCb = std::bind1st(
std::mem_fun(&Foo::catch_signal), &foo);
f(5); // this call works

sigIntHandler.sa_handler = CallCb;
sigemptyset(&sigIntHandler.sa_mask);
sigIntHandler.sa_flags = 0;
sigaction(SIGTERM, &sigIntHandler, NULL);
s.run();

}
  • Do you have any advice how to solve this kind of thing better?

Not really. The idea is ok. I would just just c++11 lambda instead

How to use std::bind function as a signal handler in C++?

The result of std::bind is an unspecified function object whose type cannot be converted into void (*)(int). Try encapsulating it:

void handler_foo(int signal)
{
return terminate_or_interrupt_handler(signal_flag, signal);
}

Or, if C++11 is available, a lambda might be better:

signal(SIGTERM, [](int signal) { return terminate_or_interrupt_handler(signal_flag, signal); });

Note that since signal_flag is a global variable (namespace-scope variable), no capture is required. A non-capturing lambda can be implicitly converted into the corresponding function pointer type.

Interruption handling with non-static member function

No, you can not do this.

The reason is that all member functions have an "implied/hidden" this pointer argument. If we "flattened" out your handler definition to produce the C equivalent, it would look like:

void SocketServer::signalHandler(SocketServer *this,int sig);

The signal function [in C] knows nothing of this [pun intended]. If it compiled, the handler would be called with sig going into the this argument and not the sig argument.

So, you really must do:

SocketServer my_global_server;

void
my_handler(int sig)
{

my_global_server.signalHandler(sig);
}

int
main(void)
{

signal(SIGABRT,my_handler);

return 0;
}

Actually, the above is quite dangerous because my_global_server may be in an indeterminate state when the signal handler is called, causing UB. Also, when in a signal handler, there are a limited number of things you are permitted to do. For example, no heap manipulations are permitted.

Here is a better way to implement this:

volatile int signal_flag;

SocketServer my_global_server;

void
my_handler(int sig)
{

signal_flag = sig;
}

int
main(void)
{

signal(SIGABRT,my_handler);

while (! signal_flag) {
...
}

my_global_server.signalHandler(signal_flag);

return 0;
}

How to register a signal handler as a class method?

Being a C function, signal can only take a plain function pointer, not arbitrary callable types. You'll need a non-member wrapper function, and a global variable to store this, in order to call a member function from a signal handler.

static A * signal_object;
extern "C" void signal_handler(int signum) {signal_object->f(signum);}

// later...
signal_object = this;
signal(SIGSEGV, signal_handler);


Related Topics



Leave a reply



Submit