How do I assign a std::function type to sigevent.sigev_notify_function (POSIX timers)

Viewed 649

I'm using a POSIX timer to call a function that is defined with std::function template:

typedef std::function<void (union sigval)> functionCbType;

I get the following error in assignment to field sigev_notify_function of struct sigevent:

error: cannot convert ‘functionCbType {aka std::function}’ to ‘void ()(sigval_t) {aka void ()(sigval)}’ in assignment se.sigev_notify_function = callcbk;

functionCbType callcbk;
...
struct sigevent se;
se.sigev_notify = SIGEV_THREAD;
se.sigev_notify_function = callcbk;
se.sigev_notify_attributes = NULL;

int status = timer_create(CLOCK_MONOTONIC, &se, &timer_id);

se.sigev_notify_function expects

void       (*sigev_notify_function) (union sigval);

What is the correct way to do this?

2 Answers

A well designed C callback API lets you pass a value to the callback function. This lets you have stateful callbacks.

In this case, union sigval is a union of an int and a void*. You are supposed to stick your state in either of those two.

std::function<Sig> can store state, so you need to pass that state through.

A std::function is not a function pointer. In order to pass it to a C function callback API, you need to wrap it up and pass it as the void* portion.

struct sig_cb {
  void(* function)(sigval_t);
  void* pvoid;
};
sig_cb make_signal_callback( std::function<void()>* pf ) {
  return {
    [](sigval_t v) { (*static_cast<std::function<void()>*>(v.sival_ptr))(); },
    pf
  };
}

then modify your code:

functionCbType callcbk;
...
struct sigevent se;
auto cb = make_signal_callback(&callcbk);
se.sigev_notify = SIGEV_THREAD;
se.sigev_notify_function = cb.function;
se.sigev_value.sival_ptr = cb.pvoid;
se.sigev_notify_attributes = NULL;

int status = timer_create(CLOCK_MONOTONIC, &se, &timer_id);

and it should work. Naturally the lifetime of your std::function must be managed separately.

The sigevent structures sigev_notify_function is not expecting a std::function object, it is expecting a function pointer:

void       (*sigev_notify_function) (union sigval);

A std::function object can be created implicitly from a function pointer, but the reverse is not possible. You must use a pointer to the actual function.

You can achieve this by using callcbk.target<void(*)(union sigval)>() to get a pointer to the original function pointer, but then callcbk is somewhat pointless.

Related