簡體   English   中英

函數指針從std :: function :: target <>()和普通函數指針之間的區別是什么?

[英]what's the difference between the function pointer getting from std::function::target<>() and normal function pointer?

以下代碼是從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;

演示


然而,真正的答案是這樣做沒有多大意義。 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的調用。 但是那個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()將你限制為只是函數指針,所以你應該將它的接口限制為只是函數指針。 非常喜歡你以前擁有的東西。 使用類型系統來捕獲錯誤 - 只在有意義時才使用類型擦除。

這里有一個例子,可以幫助您理解機制。

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

暫無
暫無

聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.

 
粵ICP備18138465號  © 2020-2024 STACKOOM.COM