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

6

38

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.

Adonic answered 5/12, 2008 at 8:42 Comment(3)
Beware that Signals are not threadsafe, so you might not want to reconsider this if necesarryGonzalogoo
The reason you need to use a static method (as described below), is that to call a method you need an instance of the class (an object). There is no way to pass an object to signal so it must be a normal function pointer (not a method pointer).Inebriant
@Robert Gould: True; but not really relevant to the subject.Inebriant
J
48

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

Janessa answered 5/12, 2008 at 16:56 Comment(5)
Your second example isn't standard C++.Ausgleich
Is that because I forgot the static keyword? If so, it's corrected!Janessa
this is the error that I am getting when I followed your second method error: 'instance' was not declared in this scopeRiendeau
@molecule: instance is declared at the end of the class, in the private section.Janessa
I tried #2 as well as a solution and getting the same issue with the undefined reference to `myClass::instance'. And I did change the myClass to the class I was using. I know c++ but this issue is throwing me for a loop.Cortex
W
12

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.

Willi answered 5/12, 2008 at 9:1 Comment(0)
O
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.)

Ophite answered 31/1, 2012 at 19:55 Comment(0)
X
3
#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();
 }
}
Xerophthalmia answered 24/9, 2015 at 9:13 Comment(1)
While this code may answer the question, providing additional context regarding why and/or how this code answers the question improves its long-term value.Paralytic
M
1

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

Mingrelian answered 4/4, 2018 at 8:42 Comment(0)
M
0
#include <signal.h>
#include <iostream>
#include <vector>

class myClass {
 public:
     myClass(){  }
     void callBack() { std::cout << "i am calling in signal" << std::endl; }
     void addVectorData(int d) {exam_vector.push_back(d);} 
 private:
     std::vector<int> exam_vector;
};

myClass * class_ptr = nullptr;
void signalHandler(int);

int main (){
    myClass cls;
    class_ptr = &cls;
    signal(3,signalHandler);

    raise(3);

    return 0;
}

void signalHandler(int sig){
    class_ptr->addVectorData(sig);
    class_ptr->callBack();
}

You can call or use class elements with signals like this.

Molality answered 16/4 at 11:53 Comment(0)

© 2022 - 2024 — McMap. All rights reserved.