2

I'm trying to use std::bind to use a member function as a callback to sigaction in the termios.h header. I understand special care needs taken with member functions and have read and followed the examples here and here, with no such luck.

Because I can pass through a static function, I thought if I left the function static, added a second variable as a pointer to itself (this), I would be good, but no such luck:

// In SerialListener.h
static void callback(int status, SerialListener *ptr);

// In the serial listener constructor

// Set callback
auto cb = std::bind(&SerialListener::callback, std::placeholders::_1, this);
sigAct_.sa_handler = cb;

But the error is as follows:

error: cannot convert ‘std::_Bind<void (*std::_Placeholder<1>, SerialListener*))(int, SerialListener*)>’ to ‘__sighandler_t {aka void (*)(int)}’ in assignment sigAct_.sa_handler = cb;

I also tried the variation in the second example w/o static implementation:

// In SerialListener.h
void callback(int status);

// In the serial listener constructor

// Set callback
auto cb = std::bind(&SerialListener::callback, this, std::placeholders::_1);
sigAct_.sa_handler = cb;

Which produces...

error: cannot convert ‘std::_Bind<std::_Mem_fn<void (SerialListener::*)(int)>(SerialListener*, std::_Placeholder<1>)>’ to ‘__sighandler_t {aka void (*)(int)}’ in assignment sigAct_.sa_handler = cb;

Errors look pretty similar, looks like it can't implicitly convert the bind to the handler it needs, but when I look at it from a perspective of input types and return types it should work. What am I missing?

Vittorio Romeo
  • 90,666
  • 33
  • 258
  • 416
DrTarr
  • 922
  • 2
  • 15
  • 34
  • @NageG - `static` member of classes/structs are more as normal function: aren't bounded to a specific object and don't know `this`; the `this` treatment regard not-static members. – max66 Nov 20 '17 at 12:14
  • 2
    Bind returns a sort of closure *object*. It's not convertible to a function pointer. You can't sneak another parameter for `__sighandler_t ` into the call. – StoryTeller - Unslander Monica Nov 20 '17 at 12:14
  • Isn't `sigaction` from `signal.h`? – user7860670 Nov 20 '17 at 12:21
  • I tried also using `this` and a void pointer in the static function instead, shouldn't `this` implicitly convert to a void pointer and then I can cast it back to the `SerialListener*`? I also tried that and it didn't work, but I don't understand why. – DrTarr Nov 20 '17 at 12:21
  • 1
    As far as I know there's no way to get a "user data" pointer into a signal handler, and you need to resort to using a variable that's visible from the handler. – molbdnilo Nov 20 '17 at 12:21
  • @VTT, yes you're right. Sorry, I'm using it with a serial port interrupt and that's why I thought it was `termios.h` – DrTarr Nov 20 '17 at 12:21
  • @NateG: Are you allowed to modify the interface of your callback, perhaps to use a `std::function` instead? It will perform the necessary type-erasure we need for everything to work seamlessly, else you're stuck with the brittle solution like VTT's answer (a static variable that you hope nobody overwrites before you need to use it) – AndyG Nov 20 '17 at 12:38
  • Yes, I can do whatever i want with my callback. Ultimately I have a non-member function outside the class who's only argument is the file descriptor for the serial port (which is a member of the class). – DrTarr Nov 20 '17 at 12:48
  • @AndyG, what would the std::function implementation look like? – DrTarr Nov 20 '17 at 13:01
  • @NateG instead of declaring `sa_handler` to be `void (*)(int)`, try declaring it to be `std::function`. Then you should be able to assign a lambda to it like `sa_handler = [this](int signal_code){callback(signal_code, this);}` – AndyG Nov 20 '17 at 13:04
  • Ah, I misunderstood, I thought you were talking about my member function. No, the sa_handler type i defined in the `signa.h` header, it's fixed. – DrTarr Nov 20 '17 at 13:14

1 Answers1

2

Neither lambda or bind can help you to set a function not matching signal handler signature as a signal handler. Note that signal handler function signature does not provide any means to pass a class instance through opaque pointer. The best you can do is to store a pointer to handling context object at some static variable:

SerialListener * p_listener;

void On_Signal(int signal_code)
{
    if(p_listener)
    {
         p_listener->DealWith(signal_code); // calling member function
    }
}

p_listener= &my_listener;

sigAct_.sa_handler = &On_Signal;

Also note that depending on nature of signal the range of actions allowed in signal handler may be very limited.

user7860670
  • 35,849
  • 4
  • 58
  • 84
  • In the example above, if I have multiple instances of `SerialListener`, how can manage the `p_listerner` pointe iff its not passed as an argument to the function? – DrTarr Nov 20 '17 at 12:34
  • 1
    @NateG Well, multiple instances of `SerialListener` can not listen to the same signal at the same time. If they are supposed to work from different threads you can use thread local static variable. If they are supposed to deal with different signals then you will need to write separate handlers for each `SerialListener`, each using its own static variable. – user7860670 Nov 20 '17 at 12:39
  • Ok, I understand. What's tricky is the serial port specific code was outside of the SerialListener class, this approach is the opposite of what I was going for. I am passing a pointer to a non-member function to the SerialListener class, then it was my hope to inside of that class associate my external callback with that serial port. It looks like my choices are limited, and I need to revise the architecture a bit. – DrTarr Nov 20 '17 at 12:44