加入收藏 | 设为首页 | 会员中心 | 我要投稿 李大同 (https://www.lidatong.com.cn/)- 科技、建站、经验、云计算、5G、大数据,站长网!
当前位置: 首页 > 百科 > 正文

c – 从std :: function :: target <>()获取的函数指针与

发布时间:2020-12-14 20:00:41 所属栏目:百科 来源:网络整理
导读:以下代码是从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 newAct
以下代码是从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;
}

上面的代码在我的测试中运行正常,但我想让它更像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 ::功能<西格>是给定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的可调用.但是那个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; });

由于sigaction()将您限制为仅仅是函数指针,因此您应该将接口限制为仅仅是函数指针.非常喜欢你以前拥有的东西.使用类型系统来捕获错误 – 只在有意义时才使用类型擦除.

(编辑:李大同)

【声明】本站内容均来自网络,其相关言论仅代表作者个人观点,不代表本站立场。若无意侵犯到您的权利,请及时与联系站长删除相关内容!

    推荐文章
      热点阅读