以下代码是从APUE复制的信号实现,稍作修改
namespace
{
using signal_handler = void (*)(int);
signal_handler signal(sigset_t sig, signal_handler);
}
Signal::signal_handler Signal::signal(sigset_t sig, void (*handler)(int))
{
struct sigaction newAction, oldAction;
sigemptyset(&newAction.sa_mask);
newAction.sa_flags = 0;
newAction.sa_handler = handler;
if (sig == SIGALRM)
{
#ifdef SA_INTERRUPT
newAction.sa_flags |= SA_INTERRUPT;
#endif
}
else
{
newAction.sa_flags |= SA_RESTART;
}
if (sigaction(sig, &newAction, &oldAction) < 0)
throw std::runtime_error("signal error: cannot set a new signal handler.")
return oldAction.sa_handler;
}
Run Code Online (Sandbox Code Playgroud)
上面的代码在我的测试中运行正常,但我想让它更像C++代码,所以我将signal_handler别名更改为
using signal_handler = std::function<void (int)>;
Run Code Online (Sandbox Code Playgroud)
我也用
newAction.sa_handler = handler.target<void (int)>();
Run Code Online (Sandbox Code Playgroud)
取代
newAction.sa_handler = handler;
Run Code Online (Sandbox Code Playgroud)
现在有一个问题.我发现newAction.sa_handler之后仍然是NULL
newAction.sa_handler = handler.target<void (int)>();
Run Code Online (Sandbox Code Playgroud)
但我不知道为什么.有人可以帮我解释一下吗?谢谢.这是我的测试代码:
void usr1_handler(int sig)
{
std::cout << "SIGUSR1 happens" << std::endl;
}
void Signal::signal_test()
{
try
{
Signal::signal(SIGUSR1, usr1_handler);
}
catch (std::runtime_error &err)
{
std::cout << err.what();
return;
}
raise(SIGUSR1);
}
Run Code Online (Sandbox Code Playgroud)
即使在Xcode中运行时使用原始代码,也没有输出.相反,我手动运行可执行文件,我可以看到SIGUSR1发生在终端中.为什么?如何使用Xcode查看输出?
直接的答案是target()非常挑剔 - 你必须准确地命名目标的类型以获得指向它的指针,否则你得到一个空指针.当你设置信号时usr1_handler,这是一个指向函数(不是函数)的指针 - 它的类型void(*)(int)不是void(int).所以你只是给错了类型target().如果你改变:
handler.target<void (int)>();
Run Code Online (Sandbox Code Playgroud)
至
handler.target<void(*)(int)>();
Run Code Online (Sandbox Code Playgroud)
这会给你正确的目标.
但请注意target() 实际返回的内容:
template< class T >
T* target();
Run Code Online (Sandbox Code Playgroud)
它返回一个指向所提供类型的指针 - 在这种情况下,它将是一个void(**)(int).在进一步分配之前,您需要取消引用.就像是:
void(**p)(int) = handler.target<void(*)(int)>();
if (!p) {
// some error handling
}
newAction.sa_handler = *p;
Run Code Online (Sandbox Code Playgroud)
Demo.
然而,真正的答案是这样做没有多大意义.std::function<Sig>是一种可被调用的类型Sig- 它可以是指向函数的指针,指向成员函数的指针,甚至是任意大小的包装函数对象.这是一个非常通用的解决方案.但是sigaction不接受任何类型的通用可调用 - 它特别接受一个void(*)(int).
通过创建签名:
std::function<void(int)> signal(sigset_t sig, std::function<void(int)> );
Run Code Online (Sandbox Code Playgroud)
你正在创造一种你允许任何可赎回的错觉!所以,我可能会尝试传递类似的东西:
struct X {
void handler(int ) { ... }
};
X x;
signal(SIGUSR1, [&x](int s){ x.handler(s); });
Run Code Online (Sandbox Code Playgroud)
这是你的签名所允许的 - 我提供的是一个可以调用的int.但是那个callable不能转换为函数指针,所以它不是你可以传入的东西sigaction(),所以这只是错误的代码永远无法工作 - 这是一个有保证的运行时失败.
更糟糕的是,我可能会传递一些可转换为函数指针的东西,但可能不知道那就是你需要的东西,所以我给你错了:
// this will not work, since it's not a function pointer
signal(SIGUSR1, [](int s){ std::cout << s; });
// but this would have, if only I knew I had to do it
signal(SIGUSR1, +[](int s){ std::cout << s; });
Run Code Online (Sandbox Code Playgroud)
由于sigaction()限制你只是函数指针,你应该将它的接口限制为只是函数指针.非常喜欢你以前拥有的东西.使用类型系统来捕获错误 - 只在有意义时才使用类型擦除.
| 归档时间: |
|
| 查看次数: |
196 次 |
| 最近记录: |