以下代码是从APUE复制过来的signal实现,稍作修改
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;
}
上面的代码在我的测试中运行良好,但我想让它更像一个 C++ 代码,所以我将 signal_handler 别名更改为
using signal_handler = std::function<void (int)>;
我也用
newAction.sa_handler = handler.target<void (int)>();
替换
newAction.sa_handler = handler;
现在有一个问题。我发现 newAction.sa_handler 之后仍然是 NULL
newAction.sa_handler = handler.target<void (int)>();
但我不知道为什么。任何人都可以帮我解释一下吗?谢谢。 这是我的测试代码:
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);
}
即使在 Xcode 中运行时使用原始代码,也没有输出。相反,我手动运行可执行文件,我可以在终端中看到 SIGUSR1 发生。为什么?如何使用 Xcode 查看输出?
最佳答案
直接的答案是 target()
非常挑剔 - 您必须准确地命名目标的类型以获得指向它的指针,否则您将获得空指针。当您将信号设置为 usr1_handler
,这是一个指向函数(不是函数)的指针 - 它的类型是 void(*)(int)
, 不是 void(int)
.所以你只是给了错误的类型 target()
.如果你改变:
handler.target<void (int)>();
到
handler.target<void(*)(int)>();
这会给你正确的目标。
但请注意target()
实际上返回:
template< class T >
T* target();
它返回一个指向所提供类型的指针——在本例中为 void(**)(int)
.在进行进一步的分配之前,您需要取消引用它。像这样的东西:
void(**p)(int) = handler.target<void(*)(int)>();
if (!p) {
// some error handling
}
newAction.sa_handler = *p;
Demo .
然而,真正的答案是这样做毫无意义。 std::function<Sig>
是给定 Sig
的类型删除调用- 它可以是指向函数的指针、指向成员函数的指针,甚至是任意大小的包装函数对象。这是一个非常通用的解决方案。但是sigaction
不接受任何类型的通用可调用 - 它特别地接受 void(*)(int)
.
通过创建签名:
std::function<void(int)> signal(sigset_t sig, std::function<void(int)> );
您正在制造允许任何可调用对象的错觉!所以,我可能会尝试传递类似的内容:
struct X {
void handler(int ) { ... }
};
X x;
signal(SIGUSR1, [&x](int s){ x.handler(s); });
这是您的签名所允许的 - 我提供了一个接受 int
的可调用对象.但是那个可调用对象不能转换为函数指针,所以它不是你可以传递给 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; });
自 sigaction()
将你限制为函数指针,你应该将你的接口(interface)限制为函数指针。 强烈更喜欢你以前拥有的东西。使用类型系统来捕获错误 - 仅在有意义时才使用类型删除。
关于c++ - 从 std::function::target<>() 获取的函数指针和普通函数指针有什么区别?,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/36903601/