std :: function as sighandler_t

时间:2013-12-27 11:39:34

标签: c++ c++11 lambda function-pointers

如何指定lambda,std :: bind结果或任何其他std :: function作为unix信号函数的参数?

我正在尝试以下

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

但它不起作用,因为两者

handler1.target<void(int)>()

handler2.target<void(int)>()

return null

如果我使用自由函数指针

初始化处理程序,它会起作用
void foo(int) { ... }
std::function<void(int)> handler = foo;

但这绝对没用。我需要捕获一些局部变量,所以我需要bind或lambda。

其实我明白为什么它不起作用。文档说target函数返回指向存储函数的指针target_type() == typeid(T),否则返回空指针。我不明白如何让它发挥作用。

有什么建议吗?

4 个答案:

答案 0 :(得分:2)

由于它是由bind构建的,或者是由带有捕获数据的lambda构建的,因此target函数在typeid下工作,std::function保存它,因此无法将其转换为自由函数在运行时,而不是类型T,模板function。对于std::bind,它将是一些库类型,对于lambda,它将是一些未命名的类型。

答案 1 :(得分:1)

sighandler_t被定义为具有以下定义的函数的指针:

void func(int);

由于std :: bind和lambdas返回仿函数,因此无法直接将它们用作信号处理程序。作为一种解决方法,您可以使用自己的包装函数,如

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);
  }
};

使用SignalManager的全局实例来管理个别信号

答案 2 :(得分:1)

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

  

当抽象机器的处理被接收到信号中断时,对象的值就是这样   不是

     
      类型为volatile std::sig_atomic_t
  •   
  • 无锁原子对象(29.4)

  •   
     在执行信号处理程序期间未指定

,并且值为any   对象不在其中任何一个中   处理程序修改的两个类别变为未定义。

您可以在信号处理程序中实际获取的唯一操作是更改类型为volatile std::sig_atomic_t无锁 std::atomic的标记的值(注意并非所有std::atomic个对象都是无锁的。然后,非信号处理代码可以轮询该标志以响应信号的发生。

N3787有一些有趣的讨论,关于如何修复C ++ 11,基本上将信号处理程序作为一个概念。

答案 3 :(得分:1)

您可以使用类似于调度程序的方法,通过地图将信号编号与std::function s相关联。

您只需要一张地图即可容纳来自免费功能的std::function即可访问

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

还有一个通用处理程序(自由函数),用于将信号编号映射到该函数:

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

实施示例

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