函数指针从std:::function: target()和普通函数指针之间有什么区别?

时间:2021-08-16 13:29:44

The following code is a signal implementation copied from APUE with a little modification

下面的代码是一个从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;
}

The above code works fine during my test, but I wanted to make it more like a C++ code, so I changed signal_handler alias to

上面的代码在我的测试中运行良好,但是我想让它更像c++代码,所以我将signal_handler别名改为

using signal_handler = std::function<void (int)>;

and also I use

我也使用

newAction.sa_handler = handler.target<void (int)>();

to replace

来代替

newAction.sa_handler = handler;

and now there is a problem. I find newAction.sa_handler is still NULL after

现在有个问题。我发现newAction。sa_handler在之后仍然为空

newAction.sa_handler = handler.target<void (int)>();

but I don't know why. Anyone can help me explain this? thanks. Here is my test code:

但我不知道为什么。有人能帮我解释一下吗?谢谢。以下是我的测试代码:

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);
}

Even when using the original code when I run it in Xcode, there is no output. Instead, I run the executable file manually, I can see SIGUSR1 happens in the terminal. Why? How can I see the output using Xcode?

即使当我在Xcode中运行原始代码时,也没有输出。相反,我手动运行可执行文件,我可以看到SIGUSR1在终端中发生。为什么?如何使用Xcode查看输出?

2 个解决方案

#1


5  

The direct answer is that target() is very picky - you must name the type of the target exactly to get a pointer to it, otherwise you get a null pointer. When you set your signal to usr1_handler, that is a pointer to a function (not a function) - its type is void(*)(int), not void(int). So you're simply giving the wrong type to target(). If you change:

直接的答案是target()非常挑剔——您必须准确地命名目标的类型以获得指向它的指针,否则您将得到一个空指针。当您将信号设置为usr1_handler时,它是指向函数(而不是函数)的指针——它的类型是void(*)(int),而不是void(int)。因此,您只是给target()提供了错误的类型。如果你改变:

handler.target<void (int)>();

to

handler.target<void(*)(int)>();

that would give you the correct target.

这会给你一个正确的目标。

But note what target() actually returns:

但是请注意target()实际返回的是:

template< class T >
T* target();

It returns a pointer to the provided type - in this case that would be a void(**)(int). You'd need to dereference that before doing further assignment. Something like:

它返回一个指向所提供类型的指针——在本例中是void(**)(int)。在做进一步的作业之前,你需要取消这个选项。喜欢的东西:

void(**p)(int) = handler.target<void(*)(int)>();
if (!p) {
    // some error handling
}
newAction.sa_handler = *p;

Demo.

演示。


However, the real answer is that this makes little sense to do. std::function<Sig> is a type erased callable for the given Sig - it can be a pointer to a function, a pointer to a member function, or even a wrapped function object of arbitrary size. It is a very generic solution. But sigaction doesn't accept just any kind of generic callable - it accepts specifically a void(*)(int).

然而,真正的答案是这样做毫无意义。函数 是对给定Sig的一种可擦除的可调用类型——它可以是指向函数的指针,指向成员函数的指针,甚至是任意大小的封装函数对象。这是一个非常通用的解决方案。但是sigaction不接受任何类型的通用可调用——它只接受特定的void(*)(int)。

By creating a signature of:

通过创建以下签名:

std::function<void(int)> signal(sigset_t sig, std::function<void(int)> );

you are creating the illusion that you are allowing any callable! So, I might try to pass something like:

你正在创造一个幻觉,你正在允许任何可调用!所以,我可以试着通过一些东西,比如:

struct X {
    void handler(int ) { ... }
};

X x;
signal(SIGUSR1, [&x](int s){ x.handler(s); });

That's allowed by your signature - I'm providing a callable that takes an int. But that callable isn't convertible to a function pointer, so it's not something that you can pass into sigaction(), so this is just erroneous code that can never work - this is a guaranteed runtime failure.

这是你的签名允许的——我提供了一个获取int的可调用性,但是这个可调用性不能转换为函数指针,所以它不是你可以传递给sigaction()的东西,所以这只是一个错误的代码,永远不会工作——这是一个有保证的运行时失败。

Even worse, I might pass something that is convertible to a function pointer, but may not know that that's what you need, so I give you the wrong thing:

更糟糕的是,我可能会传递一些可转换为函数指针的东西,但可能不知道这就是你需要的,所以我给错了:

// 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; }); 

Since sigaction() limits you to just function pointers, you should limit your interface to it to just function pointers. Strongly prefer what you had before. Use the type system to catch errors - only use type erasure when it makes sense.

由于sigaction()限制您只使用函数指针,因此应该将接口限制为函数指针。强烈地喜欢你以前所拥有的。使用类型系统捕获错误——只在有意义的时候使用类型擦除。

#2


1  

Here you a little example that will help you to understand the mechanims.

这里有一个小例子可以帮助你理解这个机制。

#include <iostream>
#include <string>
#include <functional>

void printMyInt(int a)
{
    std::cout << "This is your int " << a;
}

int main()
{
  std::function<void(int)> f = printMyInt;  
  void (*const*foo)(int) = f.target<void(*)(int)>();  

  (*foo)(56);
}

#1


5  

The direct answer is that target() is very picky - you must name the type of the target exactly to get a pointer to it, otherwise you get a null pointer. When you set your signal to usr1_handler, that is a pointer to a function (not a function) - its type is void(*)(int), not void(int). So you're simply giving the wrong type to target(). If you change:

直接的答案是target()非常挑剔——您必须准确地命名目标的类型以获得指向它的指针,否则您将得到一个空指针。当您将信号设置为usr1_handler时,它是指向函数(而不是函数)的指针——它的类型是void(*)(int),而不是void(int)。因此,您只是给target()提供了错误的类型。如果你改变:

handler.target<void (int)>();

to

handler.target<void(*)(int)>();

that would give you the correct target.

这会给你一个正确的目标。

But note what target() actually returns:

但是请注意target()实际返回的是:

template< class T >
T* target();

It returns a pointer to the provided type - in this case that would be a void(**)(int). You'd need to dereference that before doing further assignment. Something like:

它返回一个指向所提供类型的指针——在本例中是void(**)(int)。在做进一步的作业之前,你需要取消这个选项。喜欢的东西:

void(**p)(int) = handler.target<void(*)(int)>();
if (!p) {
    // some error handling
}
newAction.sa_handler = *p;

Demo.

演示。


However, the real answer is that this makes little sense to do. std::function<Sig> is a type erased callable for the given Sig - it can be a pointer to a function, a pointer to a member function, or even a wrapped function object of arbitrary size. It is a very generic solution. But sigaction doesn't accept just any kind of generic callable - it accepts specifically a void(*)(int).

然而,真正的答案是这样做毫无意义。函数 是对给定Sig的一种可擦除的可调用类型——它可以是指向函数的指针,指向成员函数的指针,甚至是任意大小的封装函数对象。这是一个非常通用的解决方案。但是sigaction不接受任何类型的通用可调用——它只接受特定的void(*)(int)。

By creating a signature of:

通过创建以下签名:

std::function<void(int)> signal(sigset_t sig, std::function<void(int)> );

you are creating the illusion that you are allowing any callable! So, I might try to pass something like:

你正在创造一个幻觉,你正在允许任何可调用!所以,我可以试着通过一些东西,比如:

struct X {
    void handler(int ) { ... }
};

X x;
signal(SIGUSR1, [&x](int s){ x.handler(s); });

That's allowed by your signature - I'm providing a callable that takes an int. But that callable isn't convertible to a function pointer, so it's not something that you can pass into sigaction(), so this is just erroneous code that can never work - this is a guaranteed runtime failure.

这是你的签名允许的——我提供了一个获取int的可调用性,但是这个可调用性不能转换为函数指针,所以它不是你可以传递给sigaction()的东西,所以这只是一个错误的代码,永远不会工作——这是一个有保证的运行时失败。

Even worse, I might pass something that is convertible to a function pointer, but may not know that that's what you need, so I give you the wrong thing:

更糟糕的是,我可能会传递一些可转换为函数指针的东西,但可能不知道这就是你需要的,所以我给错了:

// 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; }); 

Since sigaction() limits you to just function pointers, you should limit your interface to it to just function pointers. Strongly prefer what you had before. Use the type system to catch errors - only use type erasure when it makes sense.

由于sigaction()限制您只使用函数指针,因此应该将接口限制为函数指针。强烈地喜欢你以前所拥有的。使用类型系统捕获错误——只在有意义的时候使用类型擦除。

#2


1  

Here you a little example that will help you to understand the mechanims.

这里有一个小例子可以帮助你理解这个机制。

#include <iostream>
#include <string>
#include <functional>

void printMyInt(int a)
{
    std::cout << "This is your int " << a;
}

int main()
{
  std::function<void(int)> f = printMyInt;  
  void (*const*foo)(int) = f.target<void(*)(int)>();  

  (*foo)(56);
}