2014-10-30 30 views
13

Non vedo alcun commento nello standard eccetto le cose relative al collegamento.'E extern "C" `una parte del tipo di una funzione?

Sebbene lo standard non dica nulla sulla convenzione di chiamata, le convenzioni di chiamata potrebbero essere diverse tra C e C++ nel mondo reale, quindi mi aspettavo che i tipi di funzione C e di C++ fossero diversi. Ma non sembra, specialmente in GCC.

#include <type_traits> 

extern "C" { 
    int c_func(int); 
} 

int cpp_func(int); 

static_assert(!std::is_same<decltype(c_func), decltype(cpp_func)>::value, 
       "It should not be the same type"); 

static_assert fallisce dal GCC ritiene tali funzioni sono dello stesso tipo.

  • È extern "C" una parte del tipo di una funzione?
  • Come si verifica se una funzione utilizza la convenzione di chiamata C o la convenzione di chiamata C++?
+0

C++ supporta ad es. sovraccarico di funzione. Due funzioni con tipi di argomenti diversi ma lo stesso nome: non può essere eseguita con il collegamento C perché il nome della funzione deve essere alterato per rimanere unico. – keltar

+0

@keltar Sì, hai ragione, e non è quello che sto chiedendo qui. Riguarda la convenzione di chiamata, non il nome di mangling. Ogni documento, Q/A su "extern" c "" che ho trovato su internet parla di manomissione dei nomi ma non della convenzione di chiamata. – kukyakya

+1

Dato che non può essere usato sui metodi (e quindi non può influenzare thiscall) - sì, dovrebbe solo disabilitare il manegging senza modificare la convenzione, a meno che non sia specificato esplicitamente altrimenti (ad esempio con il comando specifico del compilatore '__attribute__'). Entrambi i tipi che stai confrontando sono 'int (*) (int)'. – keltar

risposta

16

Lo standard chiarisce che linguaggio linkage è infatti una proprietà di una funzione tipo stessa:

Tutti i tipi di funzione, nomi di funzione con linkage esterno, e nomi di variabili con collegamento esterno avere un collegamento linguistico .

Nel caso in cui non fosse abbastanza chiaro, c'è una nota (sottolineatura mia) che rende il significato previsto inequivocabile:

[Nota: Perché il legame lingua è parte di un tipo di funzione, quando si indirizza attraverso un puntatore alla funzione C , la funzione a cui si riferisce il valore risultante è considerata una funzione C. - fine nota]

Inoltre,

Due tipi di funzione con lingua diversa legami sono tipi distinti anche se sono altrimenti identici.

Quindi la risposta alla tua prima domanda è:

  • , extern "C" fa parte del tipo di una funzione.

Tuttavia, la maggior parte dei compilatori non riesce a distinguere tra i tipi di funzioni con il collegamento di linguaggio C e C++. Questo è ad esempio un bug di vecchia data in GCC (https://gcc.gnu.org/bugzilla/show_bug.cgi?id=2316; vedere l'elenco dei duplicati). Non ho letto attentamente l'intero thread, ma sembra che un sacco di codice esistente si interromperebbe se GCC ha iniziato ad applicare la regola che sono in realtà tipi diversi. Questo è presumibilmente anche il motivo per cui anche altri compilatori non sono conformi allo standard.

Dato che, la risposta alla tua seconda domanda sembra essere:

  • Probabilmente non esiste un modo portabile per eseguire questo controllo in fase di compilazione. Ovviamente dopo la traduzione puoi sempre entrare e guardare il file dell'oggetto e vedere se il nome è stato storpiato o meno.

Ma in teoria, la sua affermazione statica dovrebbe funzionare il modo in cui si pensa che dovrebbe. Questo non è il caso nella pratica.

Addendum:

Se la mia comprensione dello standard sia corretto, quindi ad esempio la seguente funzione di modello

template <typename R, typename... A> 
void f(R(*)(A...)); 

non è possibile creare un'istanza per la produzione di una funzione che avrebbe accettato un puntatore a una funzione con collegamento linguaggio C come argomento, poiché il tipo R(*)(A...) è "puntatore alla funzione con collegamento linguaggio C++ che riprende argomenti dei tipi A... e restituisce R ".

Se i compilatori funzionano davvero in questo modo, è facile vedere come è possibile determinare genericamente se una funzione ha un collegamento di linguaggio C o C++.

Ma questo esempio dovrebbe anche chiarire quanto male il codice esistente si interromperebbe se i compilatori realmente funzionassero in questo modo.

Problemi correlati