c++ - 从 std::function::target<>() 获取的函数指针和普通函数指针有什么区别?

标签 c++ xcode

以下代码是从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/

相关文章:

C++ 'Email' 没有命名类型

c++ - 用模板专门化模板

objective-c - 构建 SourceKitten 时没有此类模块 SWXMLHash

c++ - 现在每个 Linux 发行版都附带 gcc/g++ 4.* 吗?

c++ - 为我的代码或 vim 编辑器设置对齐规则的工具或脚本

ios - 由于 Arm64 和 Armv7,构建失败?

ios - 如何从 Xcode 中的代码覆盖范围中排除 Pod

ios - iOS10 的 uiView 在 ViewController 内错位,但 iOS11 则不然

xcode - 在 xcode 10.1 上添加可本地化的字符串

c++ - 段错误将引用作为函数参数传递