2016-09-20 60 views
7

這個問題與Warning (Anachronism): Assigning void(*)(int) to extern "C" void(*)(int)有關。在列舉的問題,我們有typedef的聲明爲extern "C"函數指針:C++是否會從聲明中剝離'extern「C」'?

extern "C" { 
    typedef void (*SignalHandlerFn) (int); 
}; 

當我們試圖給它分配:

new_handler.sa_handler = (pfn ? reinterpret_cast<SignalHandlerFn>(pfn) : 
           reinterpret_cast<SignalHandlerFn>(defaultHandler)); 

這導致錯誤(行號是有點過,但上面的行產生的話):

/opt/solarisstudio12.4/bin/CC -DDEBUG -c test.cpp 
... 
"ossig.h", line 75: Warning (Anachronism): Using void(*)(int) to initialize extern "C" void(*)(int). 
"test.cpp", line 135:  Where: While instantiating "SignalHandler<5, 0>::SignalHandler(extern "C" void(*)(int), int)". 
"test.cpp", line 135:  Where: Instantiated from non-template code. 
2 Warning(s) detected. 

能告訴我最好的,extern "C"使用reinterpret_cast時被丟棄。然而,C演員按預期工作。

我相信Sun Studio 12.4(SunCC 5.13)通過defult使用C++ 03。但是我的問題同時適用於C++ 03和C++ 11,因爲我們現在看到很多都是由於GCC 4.8和4.9的普及。

C++是否會從聲明中剝離extern "C"


solaris:~$ cat test.cxx 
#include <signal.h> 

extern "C" { 
    typedef void (*SignalHandlerFn) (int); 
}; 

template <int S, bool O=false> 
struct SignalHandler 
{ 
    SignalHandler(SignalHandlerFn pfn = NULL, int flags = 0) : m_installed(false) 
    { 
    struct sigaction new_handler; 

    do 
    { 
     int ret = 0; 

     ret = sigaction (S, 0, &m_old); 
     if (ret != 0) break; // Failed 

     if (m_old.sa_handler != 0 && !O) break; 

     new_handler.sa_handler = (pfn ? reinterpret_cast<SignalHandlerFn>(pfn) : 
             reinterpret_cast<SignalHandlerFn>(&SignalHandler::NullHandler)); 
     new_handler.sa_flags = (pfn ? flags : 0); 

     ret = sigemptyset (&new_handler.sa_mask); 
     if (ret != 0) break; // Failed 

     ret = sigaction (S, &new_handler, 0); 
     if (ret != 0) break; // Failed 

     m_installed = true; 

    } while(0); 
    } 

    ~SignalHandler() 
    { 
    if (m_installed) 
     sigaction (S, &m_old, 0); 
    } 

private: 
    struct sigaction m_old; 
    bool m_installed; 

    static void NullHandler(int /*unused*/) { /* continue*/ } 

private: 
    // Not copyable 
    SignalHandler(const SignalHandler &); 
    void operator=(const SignalHandler &); 
}; 

int main(int argc, char* argv[]) 
{ 
    SignalHandler<SIGTRAP, 0> handler; 
    return 0; 
} 
+4

語言鍵是函數的類型的部分(並且可以在其上過載);不幸的是,大多數編譯器都沒有正確實現。 –

+0

謝謝Kerrek。這是否意味着SunCC是正確的,我的SignalHandlerFn錯了,其他編譯器應該拒絕它?或者這是否意味着SunCC錯誤地處理了它?對不起,問。我知道如何解決它;但我仍不清楚問題是什麼。 – jww

+0

你的'NullHandler'肯定是錯誤的,因爲類成員函數從來沒有C鏈接。事實上,GCC和Clang完全忽略了這一點。您可以仔細檢查Embarcadero,我相信這些規則會正確實施這些規則。另一方面,'pfn'應該是正確的。 –

回答

4

reinterpret_cast<T>要麼產生T類型的表達式,或由於現有沒有允許轉換是形成不良的。 (ref:[expr.reinterpret.cast]/1)。

語言鏈接是類型的一部分(ref:[dcl.link]/1)。

所以reinterpret_cast<SignalHandlerFn>的結果要麼是格式不正確,要麼是與C語言鏈接功能的指針。

因此,將這個轉換描述爲「剝離外部C」似乎是不正確的 - 雖然編譯器當然可以通過發佈診斷來對不合格的代碼作出反應,然後繼續進行,就好像代碼有一些任意行爲。


在您的代碼示例中,既reinterpret_cast<SignalHandlerFn>用途都能很好地形成,因爲可以reinterpret_cast任何函數指針轉換爲其他任何函數指針(參照[expr.reinterpret.cast]/6)。

但是,撥打SignalHandler::NullHandlersa_handler將導致未定義的行爲(ref:同上)。編譯器產生的警告可能是爲了警告這種情況。

+0

我會重點說一下:它似乎是一種常見的編譯器行爲,允許從「extern C++」函數到「extern C」函數指針的隱式轉換。也許這是由編譯器供應商出於實際原因有目的地完成的 –

+0

因爲它不是該類型的一部分。事實上,我從來沒有見過這樣的情況,它實際上有所作爲。現在你不能將類成員函數傳遞給C,但這是一個不同的故事。 – Joshua

0

的sigaction結構體的定義爲:

struct sigaction { 
     void  (*sa_handler)(int); 
     void  (*sa_sigaction)(int, siginfo_t *, void *); 
     sigset_t sa_mask; 
     int  sa_flags; 
     void  (*sa_restorer)(void); 
    }; 

你使用結構的sigaction的無效(* sa_handler)(INT);從一些SignalHandlerFn中分配。 因此,兩個聲明都應該在extern "C"例如

extern "C" { 
    #include <signal.h> 
}; 

extern "C" { 
    typedef void (*SignalHandlerFn) (int); 
}; 

或者兩者應與支持C++的名字改編,i.ee.的無extern "C"爲:

#include <signal.h> 

typedef void (*SignalHandlerFn) (int); 
+0

'signal.h'是C標準庫函數的頭文件,聲明需要'extern「C」'鏈接(通常頭文件會檢測C++並正確設置鏈接,不需要執行'extern「 C「{#include') –

+0

僅供參考...... Solaris文檔位於[使用指針函數](http://www.oracle.com/technetwork/articles/servers-storage-dev/mixingcandcpluspluscode-305840。 html#pfn)明確指出我們不應該包含'extern「C」'。 – jww

+0

在適用的情況下,標準頭文件應該已經包含'extern「C」',這是未定義的行爲 –