2010-01-14 8 views

risposta

20

Non è sicuro secondo lo standard C++. Come indicato in this SO posting:

funzione di callback A C implementato in C++ deve essere extern "C". Potrebbe sembrare che funzioni come una funzione statica in una classe in quanto le funzioni statiche di classe utilizzano spesso la stessa convenzione di chiamata di una funzione C. Tuttavia, fare questo è un bug che aspetta di accadere (vedere i commenti sotto), quindi per favore non - passare attraverso un wrapper "C" extern invece.

E in base alle osservazioni fatte da Martin York in questa risposta ci sono problemi del mondo reale che cercano di farlo su alcune piattaforme.

Effettua i callback C ABI extern "C".


Edit: L'aggiunta di alcune citazioni di supporto dallo standard (sottolineatura mia):

3.5 "Programma e linkage":

Dopo tutte le regolazioni di tipi (durante il quale typedef (7.1. 3) sono sostituiti dalle loro definizioni), i tipi specificati da tutte le dichiarazioni relative a un oggetto o funzione determinato devono essere identici, eccetto che le dichiarazioni per un oggetto array possono specificare tipi di array che differiscono dalla presenza o dall'assenza di un array principale bou nd (8.3.4). Una violazione di questa regola sull'identità del tipo non richiede una diagnostica. [3.5/10]

[Nota: il collegamento a dichiarazioni non C++ può essere ottenuto utilizzando una specifica di collegamento (7.5). ] [3.5/11]

E

7.5 "Specifiche Linkage":

... Due tipi di funzione con diversi legami linguistici sono tipi distinti anche se sono altrimenti identici . [7.5/1]

Quindi, se il codice rendendo il callback sta usando binding di linguaggio C per la richiamata, allora l'obiettivo di richiamata (nel programma C++) must pure.

+0

Grazie per il collegamento - ancora IMO si dovrebbe notare che in pratica tutti i compilatori (tutti quelli con cui ho lavorato ...) sembrano fornire soluzioni non portatili - come una dichiarazione di convenzione di chiamata - per risolvere i problemi. – peterchen

+0

@peterchen: i problemi possono essere risolti facilmente usando 'extern" C "', o c'è qualcosa che mi manca? –

+0

"Per lo standard C++"? Quale parte dello standard dice questo? –

5

Per tutti i compilatori Windows C++ di cui sono a conoscenza, la risposta è sì, ma nulla nello standard della lingua lo garantisce. Tuttavia, non mi permetto di fermarti, è un modo molto comune di implementare i callback usando C++ - potresti trovare che devi comunque dichiarare le funzioni statiche come WINAPI. Questo è preso da una vecchia libreria di threading della mia:

class Thread { 
    ... 
    static DWORD WINAPI ThreadFunction(void * args); 
}; 

in cui questo è l'uso di callback da te API di Windows threading.

+0

Ho perso il collegamento ma GCC (nell'ultima versione) usa anche la stessa convenzione di chiamata per i metodi statici e le funzioni C, quindi anche gli utenti di gcc sono al sicuro. –

+4

Non vedo davvero il punto in questo però. Voglio dire, se la funzione è un membro statico * comunque *, perché non limitarsi a giocare in modo sicuro e farlo passare attraverso una funzione non membro, in primo luogo? – jalf

+0

jalf: Perché hai ancora solo l'illusione della sicurezza, dato che sei al capriccio dell'implementazione. Apparentemente è un * piccolo * un po 'più portatile (non ho ancora sentito di nuovo su quali compilatori questo influisce), ma, come sono sicuro tu sai, questo non è lo stesso di quello garantito dallo standard. Perché ci si impegna a risolvere problemi che * non sono ancora presenti * nella tua implementazione e che non ti aspetti di influenzare nei prossimi 5 anni? –

3

ABI non è coperto dagli standard C o C++, anche se C++ fornisce "collegamento linguistico" tramite extern "C". Pertanto, l'ABI è fondamentalmente compilatore/piattaforma specifico.Entrambi gli standard lasciano molte, molte cose all'attuazione, e questo è uno di questi.

Di conseguenza, la scrittura di codice portatile 100% — o la conversione di compilatori — è difficile da impossibile, ma consente ai fornitori e agli utenti una notevole flessibilità nei loro prodotti specifici. Questa flessibilità consente più programmi efficienti in termini di spazio e tempo, in modi che non devono essere anticipati in anticipo dai comitati standard.


Da quanto ho capito, le regole di ISO non consentono uno standard più spesso di una volta ogni 10 anni (ma ci possono essere varie pubblicazioni, come TC1 e TR1 per C++). Inoltre, c'è l'idea (non sono sicuro che provenga dall'ISO, sia ripresa dal comitato C, o anche da altrove) per "distillare"/standardizzare le pratiche esistenti piuttosto che andare in campo a sinistra, e ci sono molte pratiche esistenti, alcune delle quali in conflitto.

+1

Beh, non sono d'accordo con Martin, e gli anni di esperienza di programmazione di Windows sembrano confermare questo.E come si osserva, non esiste ABI standard, quindi l'utilizzo di una funzione C non ha nemmeno garanzie. –

+0

se stai usando solo compilatori Windows, starai bene ... fino a quando non porti il ​​tuo codice su Android o Linux o Mac o altro, allora potresti scoprire che il tuo codice "funzionante" non funziona. Più sicuro usare extern C - non è esattamente molto lavoro extra. – gbjbaanb

+0

Neil: esattamente. Il meglio che puoi fare è dire "assicurati di fare ciò che il tuo compilatore richiede". –

6

Dopo la ricerca e diverse interruzioni mentre attacca altri problemi, ho trovato una risposta che è chiara e concisa (per standardese, comunque):

Chiamata di una funzione attraverso un'espressione il cui tipo di funzione ha un legame linguaggio che è diverso dal collegamento linguistico del tipo di funzione della definizione della funzione chiamata non definito. [5.2.2/1]

Continuo a sostenere che si tratta di un problema a un livello fondamentale per utilizzare il testo dalla standard C++ per definire il comportamento di una libreria C compilato con un compilatore C, ed esattamente come che dell'interlingua le opere di interoperabilità sono molto specifiche per l'implementazione; tuttavia, questo è il più vicino credo che uno standard possa (attualmente) sperare di definire tale interazione.

In particolare, questo è comportamento non definito (e non utilizza una libreria C in modo tale questione non si pone):

void call(void (*pf)()) { pf(); } // pf() is the UB 
extern "C" void f(); 
int main() { call(f); } 
// though I'm unsure if a diagnostic is required for call(f) 

Comeau dà una diagnosi a call(f) (anche se può farlo anche se la diagnostica non è richiesta).

Questo non è un comportamento indefinito, e mostra come includere la lingua linkage in un tipo di puntatore a funzione (che è attraverso un typedef):

extern "C" typedef void F(); 
void call(F* pf) { pf(); } 
extern "C" void f(); 
int main() { call(f); } 

o potrebbe essere scritto:

extern "C" { 
typedef void F(); 
void f(); 
} 
void call(F* pf) { pf(); } 
int main() { call(f); } 
Problemi correlati