简体   繁体   中英

Catching signals: Use a member function as signal handler

I have an object which does some work in an endless loop. The main() instantiates the object and calls the run() method. Since I don't want to use threads, I need a solution to make my object stop running. Below you see what I've come up with.

struct Foo
{
    void run()
    {
        running = 1;
        while (running) 
            do_something_useful();

        std::cout << "Execution stopped." << std::endl;
    }

    bool running;

    void catch_signal(int signal)
    {
        std::cout << "Caught signal " << signal << std::endl;
        if( signal == SIGTERM ) 
            running = false;
    }

};

As you see, I need to send a signal asynchronously. Therefore, I use a signal handler and sigaction . Below the main I can imagine to use.

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

    boost::function< void (int) > f;
    f = std::bind1st(
      std::mem_fun(&Foo::catch_signal), &foo);
    f(5);  // this call works

    sigIntHandler.sa_handler = f;           // compiler complains, "cannot assign ..."
    sigemptyset(&sigIntHandler.sa_mask);
    sigIntHandler.sa_flags = 0;
    sigaction(SIGTERM, &sigIntHandler, NULL);
    s.run();

}

What I would expect now: The program runs until I send SIGTERM which is caught and will cause my object to stop iteration and return to main.

I have two questions now:

(a) In the code you see a line marked with "Compiler complains", the message is like

boost::function<void(int)> cannot be converted to __sighandler_t {aka void (*)(int)}

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.

(b) For those of you who wonder "what is that guy doing?": Do you have any advice how to solve this kind of thing better?

  • 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

There is very little that you can do portably in a signal handler. Basically, you can store a value in an object whose type is sig_atomic_t . Inserting into cout , for example, doesn't have to work. If you have C++11 you can do a bit more, using atomic types or explicit fences, but any other calls into the standard library aren't required to do anything sensible.

So, with all that said, what you can do is write a function (either a free function or a static member function (but there is a subtle issue here with C++ linkage: formally a static member function won't work, but in practice it always does)) that calls the member function, which in turn sets running to false (assuming you've changed the type of running to sig_atomic_t ).

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM