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

2019-01-10 14:48发布

问题:

I am doing something like this:

#include <signal.h>

class myClass {

    public: 

    void myFunction () 
    {
    signal(SIGIO,myHandler);
    }

    void myHandler (int signum)
    {
    /**
    * Handling code
    */
    }

    }

I am working on Ubuntu, using gcc.

But it won't compile. It is complaining with:

error: the argument with type void (MyClass::)(int) doesn't agree with void (*) (int)

Any clues? Or maybe it is just that I cannot use a signal inside classes? Are signals only allowed in C?

The error message is an approximate translation because my compiler is not in English.

回答1:

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 :-)!



回答2:

To pass a pointer to a method, it must be a static method and you must specify the class name.

Try this:

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

  static void myHandler (int signum)
  {
     // blabla
  }
};

And you should also read the link supplied by Baget, the paragraph 33.2 in the C++ FAQ.



回答3:

Actually, C++ signal handlers are not permitted to use any facilities not present in both C and C++ (except that in C++11 they may use atomics), and are required to use C linkage. Quoting C++11 draft n3242 section 18.10 "Other runtime support" [support.runtime] (paragraph 8),

The common subset of the C and C++ languages consists of all declarations, definitions, and expressions that may appear in a well formed C++ program and also in a conforming C program. A POF (“plain old function”) is a function that uses only features from this common subset, and that does not directly or indirectly use any function that is not a POF, except that it may use functions defined in Clause 29 that are not member functions. All signal handlers shall have C linkage. A POF that could be used as a signal handler in a conforming C program does not produce undefined behavior when used as a signal handler in a C++ program. The behavior of any other function used as a signal handler in a C++ program is implementation-defined.

(Clause 29 being the one on atomics.)



回答4:

#include <signal.h>

class myClass {

 private:
  static myClass* me;

 public:
  myClass(){ me=this; }

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

  void my_method(){ }

  static void myHandler (int signum){
    me->my_method();
 }
}


回答5:

Read this following section (33.2):

C++ FAQ - pointers-to-members



回答6:

It does seem crazy that you can post new answers with less reputation than it takes to comment on existing ones, but there we go.

gekomad's answer from Sept 24 2015 was the one that I used to solve my problem. It is worth pointing out that this will only work completely obviously when there is only ever one instance of myClass created. otherwise, the static object pointer will point to one of the instances (the most recently created) which may not be the desired one.

And, in case it is useful to someone else, a valid 2018 URL for the FAQ question linked in a couple of the answers is:

http://www.cs.technion.ac.il/users/yechiel/c++-faq/memfnptr-vs-fnptr.html