2010-09-24 15 views
226

Sto lavorando a un progetto che contiene molto codice C. Abbiamo iniziato a scrivere in C++, con l'intento di convertire anche il codice legacy. Sono un po 'confuso su come interagiscono il C e il C++. Comprendo che, inserendo il codice C con extern "C", il compilatore C++ non manipolerà i nomi del codice C, ma non sono completamente sicuro di come implementarlo.Combinare C++ e C - come funziona #ifdef __cplusplus?

Così, nella parte superiore di ogni C file di di intestazione (dopo l'inclusione guardie), abbiamo

#ifdef __cplusplus 
extern "C" { 
#endif 

e in fondo, scriviamo

#ifdef __cplusplus 
} 
#endif 

tra i due , abbiamo tutti i nostri inclusi, typedef e prototipi di funzioni. Ho un paio di domande, per vedere se sto capire questo correttamente:

  1. Se ho un C++ il file A.hh che include un C file di intestazione Bh, include un altro C intestazione file Ch, come funziona? Credo che quando i passaggi compilatore in B.h, saranno definiti __cplusplus, quindi sarà avvolgere il codice con extern "C" (e __cplusplus non saranno definita all'interno di questo blocco). Quindi, quando entra in C.h, __cplusplus non sarà definito e il codice non verrà incapsulato in extern "C". È corretto?

  2. C'è qualcosa che non va con che avvolge un pezzo di codice con extern "C" { extern "C" { .. } }? Cosa farà il secondo extern "C" ?

  3. Non mettiamo questo wrapper attorno ai file .c, solo i file .h. Quindi, cosa succede se una funzione non ha un prototipo? Il compilatore pensa che sia una funzione C++?

  4. Stiamo anche utilizzando alcuni codice di terze parti che è scritto in C , e non non abbiamo questo tipo di involucro intorno esso. Ogni volta che includo un'intestazione da quella libreria, ho inserito uno extern "C" in tutto il #include. È questo il modo giusto per gestire lo ?

  5. Infine, questa è una buona idea? C'è qualcos'altro che dovremmo fare? Stiamo miscelando C e C++ per il prossimo futuro, e io vogliamo assicurarci che stiamo coprendo tutte le nostre basi .

+6

Dai un'occhiata a [C++ FQA: mixing C e C++] (http://yosefk.com/c++fqa/mixing.html) e [C++ FAQ: mixing C e C++] (http: // www .parashift.com/C++ - faq-lite/mixing-c-e-cpp.html). – Lazer

+0

Concisamente, questa è la spiegazione migliore: 'Per garantire che i nomi dichiarati in quella parte di codice abbiano il collegamento C, e quindi il manichino del nome C++ non viene eseguito. (L'ho preso da [il collegamento] (https : //cboard.cprogramming.com/c-programming/150020-whats-purpose-sharpifdef-__cplusplus.html)) – anhldbk

risposta

207

extern "C" non modifica realmente il modo in cui il compilatore legge il codice. Se il tuo codice è in un file .c, sarà compilato come C, se è in un file .cpp, verrà compilato come C++ (a meno che tu non faccia qualcosa di strano nella tua configurazione).

Che cosa è extern "C" influisce sul collegamento. Le funzioni del C++, quando compilate, hanno i loro nomi storpiati - questo è ciò che rende possibile il sovraccarico. Il nome della funzione viene modificato in base al tipo e al numero di parametri, in modo che due funzioni con lo stesso nome abbiano nomi di simboli diversi.

Il codice all'interno di extern "C" è ancora codice C++. Esistono limitazioni su cosa è possibile fare in un blocco "C" esterno, ma riguardano esclusivamente il collegamento. Non è possibile definire nuovi simboli che non possono essere creati con il collegamento C. Ciò significa che non ci sono classi o modelli, per esempio.

extern "C" blocchi nidificano bene. C'è anche extern "C++" se ti trovi irrimediabilmente intrappolato all'interno delle regioni extern "C", ma non è una buona idea dal punto di vista della pulizia.

Ora, in particolare per quanto riguarda le vostre domande numerate:

Riguardo # 1: __cplusplus dovrebbero essere definite all'interno di extern "C" blocchi. Questo non importa, però, dato che i blocchi dovrebbero nidificare perfettamente.

Per quanto riguarda il n. 2: __cplusplus verrà definito per qualsiasi unità di compilazione che viene eseguita tramite il compilatore C++. In genere, ciò significa file .cpp e qualsiasi file incluso da quel file .cpp. Lo stesso file .h (o .hh o .hpp o what-have-you) potrebbe essere interpretato come C o C++ in momenti diversi, se diverse unità di compilazione li includono. Se si desidera che i prototipi nel file .h facciano riferimento ai nomi dei simboli C, devono avere extern "C" quando vengono interpretati come C++ e non dovrebbero avere extern "C" quando vengono interpretati come C - da cui il controllo #ifdef __cplusplus.

Per rispondere alla domanda n. 3: le funzioni senza prototipi avranno il collegamento C++ se sono in file .cpp e non all'interno di un blocco extern "C". Questo va bene, però, perché se non ha un prototipo, può essere chiamato solo da altre funzioni nello stesso file, e di solito non ti interessa come appare il collegamento, perché non hai intenzione di avere quella funzione essere chiamato da qualsiasi cosa al di fuori della stessa unità di compilazione comunque.

Per il n. 4, l'hai esattamente. Se si include un'intestazione per il codice che presenta il collegamento C (ad esempio il codice compilato da un compilatore C), l'intestazione deve essere extern "C", in questo modo sarà possibile collegarsi alla libreria. (In caso contrario, il vostro linker sarebbe in cerca di funzioni con nomi come _Z1hic quando si stavano cercando void h(int, char)

5: Questo tipo di miscelazione è un motivo comune per utilizzare extern "C", e non vedo nulla di sbagliato nel fare in questo modo -. basta essere sicuri di capire cosa si sta facendo

+3

Buono per menzionare 'extern" C++ "' quando l'intestazione/codice C++ viene intrappolato in profondità all'interno di un codice C – deddebme

+1

Ho scritto un semplice programma C. Al suo interno ho aggiunto il blocco #ifdef __cplusplus e aggiunto un printf ("__ cplusplus definito \ n"); dentro. Se lo compilo con gcc, "__cplusplus defined" non viene stampato, ma se lo compilo con g ++, viene stampato. Quindi immagino che __cplusplus significhi che il compilatore è compilatore C++ (lo hai detto tu). Non è corretto? (perché ti ho visto dire che __cplusplus deve essere definito all'interno dei "blocchi" extern. "possiamo definire esplicitamente __cplusplus? –

+0

Mentre dovresti essere in grado di definire (quasi) tutto ciò che vuoi, l'intero punto di' __cplusplus' è determinare se 'C++' viene usato vs 'C', quindi definirlo manualmente/esplicitamente sfida il suo scopo ... – nurchi

31
  1. extern "C" non cambia la presenza o l'assenza del __cplusplus macro. Cambia solo il collegamento e la manipolazione dei nomi delle dichiarazioni avvolte.

  2. È possibile nidificare i blocchi extern "C" piuttosto felicemente.

  3. Se si compila il file .c come C++, allora tutto quanto non in un blocco extern "C", e senza un extern "C" prototipo sarà trattata come una funzione C++. Se li compili come C, ovviamente tutto sarà una funzione C.

  4. Si può tranquillamente mescolare C e C++ in questo modo.

15

un paio di grattacapi che sono colloraries a risposta eccellente di Andrew Shelansky e per dissentire un po 'con in realtà non cambia il modo in cui il compilatore legge il codice

Poiché i prototipi di funzione sono compilati come C, si ca non ha l'overloading degli stessi nomi di funzioni con parametri diversi - questa è una delle caratteristiche chiave del mangling del nome del compilatore. È descritto come un problema di collegamento ma non è del tutto vero: si otterranno errori sia dal compilatore che dal linker.

Gli errori del compilatore saranno se si tenta di utilizzare le funzionalità C++ della dichiarazione di prototipo come l'overloading.

Il linker errori si verificheranno più tardi perché apparirà la funzione di non essere trovata, se lo fai non hanno il extern "C" wrapper per le dichiarazioni e l'intestazione è incluso in un misto di C e C fonte ++ .

Un motivo per scoraggiare le persone dall'utilizzare la C come C++ è perché questo significa che il loro codice sorgente non è più portatile. Quella impostazione è un'impostazione di progetto e quindi se un file .c viene rilasciato in un altro progetto, non verrà compilato come C++. Preferirei che le persone prendessero il tempo necessario per rinominare i suffissi dei file in .cpp.

+1

Questa è stata la causa criptica, che mi tirava fuori i capelli. Ha davvero bisogno di essere postata da qualche parte. –