如何指定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)
,否则返回空指针。我不明白如何让它发挥作用。
有什么建议吗?
答案 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