std::function as sighandler_t

社会主义新天地 提交于 2019-12-23 23:34:28

问题


How to specify lambda, std::bind result or any other std::function as argument for unix signal function?

I'm trying the following

std::function<void(int)> handler1 = std::bind(&cancellation_token::cancel, &c);
std::function<void(int)> handler2 = [&c](int) { c.cancel(); };

but it doesn't work, because both

handler1.target<void(int)>()

and

handler2.target<void(int)>()

return null

It works if I initialize handler with free function pointer

void foo(int) { ... }
std::function<void(int)> handler = foo;

but this is absolutely useless. I need to capture some local variables, so I need either bind or lambda.

Actually I understand why it doesn't work. Documentation says that target function returns a pointer to the stored function if target_type() == typeid(T), otherwise a null pointer. I don't understand how to make it work.

Any suggestions?


回答1:


Since it's constructed by bind, or lambda with captured-data, you cannot convert it to free function, since target function works by typeid, std::function saves it in runtime, not for type T, with which function is templated. For std::bind it will be some library-type and for lambda it will be some unnamed type.




回答2:


sighandler_t is defined to be a pointer to a function with the following definition:

void func(int);

Since std::bind and lambdas return functors, it is not possible to directly use them as signal handler. As a workaround you can use your own wrapper functions like

class SignalHandlerBase
{
public:
  virtual void operator(int) = 0;
};

template <class T>
class SignalHandler : public SignalHandlerBase
{
  T t;
public:
  SignalHandler(T _t) : t(_t) { }
  void operator(int i)
  {
    t(i);
  } 
};

class SignalManager
{
  int sig;
  SignalHandlerBase *shb;
  static void handlerFunction(int i)
  {
    shb(i);
  }
public:
  SignalManager(int signal) : sig(signal), shb(nullptr) { signal(signal, &handlerFunction); }
  template <class T>
  void installHandler(T t)
  {
    delete shb;
    shb = new SignalHandler<T>(t);
  }
};

Use global instances of SignalManager to manage individual signals




回答3:


C++11 1.9 [intro.execution]/6:

When the processing of the abstract machine is interrupted by receipt of a signal, the values of objects which are neither

  • of type volatile std::sig_atomic_t nor

  • lock-free atomic objects (29.4)

are unspecified during the execution of the signal handler, and the value of any object not in either of these two categories that is modified by the handler becomes undefined.

The only action you can realistically take portably in a signal handler is to change the value of a flag whose type is volatile std::sig_atomic_t or a lock-free std::atomic (Note that not all std::atomic objects are lock-free). Non-signal handling code can then poll that flag to respond to the occurrence of the signal.

N3787 has some interesting discussion about how to fix C++11 basically breaking signal handlers as a concept.




回答4:


You can use a dispatcher-like approach associating signal numbers to std::functions through a map.

You just need a map to hold the std::functions accesible from a free function:

std::unordered_map<int, std::function<void(int)>> signalHandlers;

And a generic handler (free function) to map the signal number to the function:

void dispatcher(int signal) {
  // this will call a previously saved function
  signalHandlers.at(signal)(signal);
}

Implementation example

main.cpp

#include <iostream>
#include <thread>
#include <csignal>
#include "cppsignal.hpp"

int main() {
  bool stop = false;
  // set a handler as easy as this
  CppSignal::setHandler(SIGINT, [&stop] (int) { stop = true; });

  while (!stop) {
    std::this_thread::sleep_for(std::chrono::seconds(1));
  }
  std::cout << "Bye" << std::endl;
  return 0;
}

cppsignal.cpp

#include <cstring> // strsignal
#include <csignal>
#include <string>
#include <stdexcept>
#include <unordered_map>
#include <mutex>
#include "signal.hpp"

namespace CppSignal {

std::timed_mutex signalHandlersMutex;
std::unordered_map<int, std::function<void(int)>> signalHandlers;

// generic handler (free function) to set as a handler for any signal
void dispatcher(int signal) {
  std::unique_lock<std::timed_mutex> lock(signalHandlersMutex, std::defer_lock);
  if (!lock.try_lock_for(std::chrono::seconds(1))) {
    // unable to get the lock. should be a strange case
    return;
  }
  auto it = signalHandlers.find(signal);
  if (it != signalHandlers.end()) {
    it->second(signal);
  }
}

void registerHandler(int signal, const std::function<void(int)>& handler) {
  std::lock_guard<std::timed_mutex> lock(signalHandlersMutex);
  signalHandlers.emplace(signal, handler);
}

// this is the only method you will use
void setHandler(int signal, const std::function<void(int)>& handler, int flags) {
  // configure sigaction structure
  struct sigaction action;
  if (sigfillset(&action.sa_mask) == -1) {
    throw std::runtime_error("sigfillset failed");
  }
  action.sa_flags = flags;
  action.sa_handler = dispatcher;

  // set handler for the signal
  if (sigaction(signal, &action, nullptr) == -1 && signal < __SIGRTMIN) {
    throw std::runtime_error("Fail at configuring handler for signal: " + std::string(strsignal(signal)));
  }
  registerHandler(signal, handler);
}

}

cppsignal.hpp

#ifndef __CPPSIGNAL_HPP
#define __CPPSIGNAL_HPP

#include <functional>

namespace CppSignal {

void setHandler(int signal, const std::function<void(int)>& handler, int flags=0);

}

#endif


来源:https://stackoverflow.com/questions/20800055/stdfunction-as-sighandler-t

易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!