from - c++
Combinación de C++ y C: ¿cómo funciona#ifdef__cplusplus? (3)
extern "C"
no cambia la presencia o ausencia de la macro__cplusplus
. Simplemente cambia el enlace y el nombre de las declaraciones envueltas.Puedes anidar bloques
extern "C"
bastante felizmente.Si compila sus archivos
.c
como C ++, cualquier cosa que no esté en un bloqueextern "C"
y sin un prototipoextern "C"
se tratará como una función de C ++. Si los compilas como C, por supuesto, todo será una función C.Sí
Puedes mezclar de forma segura C y C ++ de esta manera.
Estoy trabajando en un proyecto que tiene muchos códigos C heredados. Empezamos a escribir en C ++, con la intención de convertir eventualmente el código heredado también. Estoy un poco confundido acerca de cómo interactúan C y C ++. Entiendo que al envolver el código C con extern "C"
el compilador de C ++ no modificará los nombres de los códigos C , pero no estoy completamente seguro de cómo implementar esto.
Por lo tanto, en la parte superior de cada archivo de encabezado C (después de las guardas de inclusión), tenemos
#ifdef __cplusplus
extern "C" {
#endif
y en el fondo escribimos
#ifdef __cplusplus
}
#endif
Entre los dos, tenemos todos nuestros prototipos de funciones, typedefs y include. Tengo algunas preguntas, para ver si estoy entendiendo esto correctamente:
Si tengo un archivo C ++ A.hh que incluye un archivo de encabezado C Bh, incluye otro archivo de encabezado C Ch, ¿cómo funciona esto? Creo que cuando el compilador entra en Bh, se
__cplusplus
, por lo que se ajustará el código conextern "C"
(y__cplusplus
no se definirá dentro de este bloque). Por lo tanto, cuando entra en Ch,__cplusplus
no se definirá y el código no se ajustará enextern "C"
. ¿Es esto correcto?¿Hay algo malo en envolver un fragmento de código con
extern "C" { extern "C" { .. } }
? ¿Qué hará la segundaextern "C"
?No colocamos este contenedor alrededor de los archivos .c, solo de los archivos .h. Entonces, ¿qué pasa si una función no tiene un prototipo? ¿El compilador piensa que es una función de C ++?
También estamos usando algún código de terceros que está escrito en C , y no tiene este tipo de envoltorio alrededor. Cada vez que incluyo un encabezado de esa biblioteca, he estado poniendo una
extern "C"
alrededor del #include. ¿Es esta la manera correcta de lidiar con eso?Finalmente, ¿es esto una buena idea? ¿Hay algo más que deberíamos hacer? Vamos a mezclar C y C ++ en el futuro inmediato, y quiero asegurarme de que estamos cubriendo todas nuestras bases.
Un par de errores que son equivalentes a la excelente respuesta de Andrew Shelansky y no estar de acuerdo un poco no cambia la forma en que el compilador lee el código
Debido a que sus prototipos de funciones se compilan como C, no puede tener la sobrecarga de los mismos nombres de funciones con diferentes parámetros, esa es una de las características clave de la manipulación del nombre del compilador. Se describe como un problema de vinculación, pero eso no es del todo cierto: obtendrá errores tanto del compilador como del vinculador.
Los errores del compilador serán si intenta usar las características de C ++ de la declaración de prototipo, como la sobrecarga.
Los errores del vinculador ocurrirán más adelante porque su función parecerá que no se encuentra, si no tiene el envoltorio externo "C" alrededor de las declaraciones y el encabezado se incluye en una mezcla de fuente C y C ++.
Una razón para desalentar a las personas a usar la configuración de compilación C como C ++ es porque esto significa que su código fuente ya no es portátil. Esa configuración es una configuración de proyecto y, por lo tanto, si un archivo .c se coloca en otro proyecto, no se compilará como c ++. Preferiría que la gente se tomara el tiempo para cambiar el nombre de los sufijos de archivos a .cpp.
extern "C"
realmente no cambia la forma en que el compilador lee el código. Si su código está en un archivo .c, se compilará como C, si está en un archivo .cpp, se compilará como C ++ (a menos que haga algo extraño en su configuración).
Lo que hace extern "C"
es afectar el enlace. Las funciones de C ++, cuando se compilan, tienen sus nombres alterados, esto es lo que hace posible la sobrecarga. El nombre de la función se modifica según los tipos y la cantidad de parámetros, de modo que dos funciones con el mismo nombre tendrán nombres de símbolos diferentes.
El código dentro de una extern "C"
todavía es un código C ++. Hay limitaciones sobre lo que puede hacer en un bloque externo "C", pero se trata de enlaces. No puede definir ningún símbolo nuevo que no pueda construirse con el enlace C. Eso significa que no hay clases o plantillas, por ejemplo.
extern "C"
bloques extern "C"
anidan muy bien. También hay extern "C++"
si te encuentras atrapado sin remedio dentro de las regiones extern "C"
, pero no es una buena idea desde el punto de vista de la limpieza.
Ahora, específicamente con respecto a sus preguntas numeradas:
Con respecto al # 1: __cplusplus debe definirse dentro de bloques extern "C"
. Esto no importa, sin embargo, ya que los bloques deben anidar perfectamente.
Con respecto al # 2: __cplusplus se definirá para cualquier unidad de compilación que se esté ejecutando a través del compilador de C ++. En general, eso significa que los archivos .cpp y cualquier archivo incluido por ese archivo .cpp. El mismo .h (o .hh o .hpp o what-have-you) podría interpretarse como C o C ++ en diferentes momentos, si diferentes unidades de compilación los incluyen. Si desea que los prototipos en el archivo .h se refieran a los nombres de los símbolos C, deben tener extern "C"
cuando se interpretan como C ++, y no deberían tener extern "C"
cuando se interpretan como C - por lo tanto, #ifdef __cplusplus
comprobación.
Para responder a su pregunta # 3: las funciones sin prototipos tendrán un enlace C ++ si están en archivos .cpp y no dentro de un bloque extern "C"
. Sin embargo, esto está bien, ya que si no tiene un prototipo, solo puede ser llamado por otras funciones en el mismo archivo, y entonces generalmente no le importa cómo se ve el enlace, porque no está planeando tener esa función Ser llamado por cualquier cosa fuera de la misma unidad de compilación de todos modos.
Para el # 4, lo tienes exactamente. Si está incluyendo un encabezado para el código que tiene un enlace C (como el código compilado por un compilador de C), debe extern "C"
el encabezado en extern "C"
, de esa manera podrá vincularlo con la biblioteca. (De lo contrario, su vinculador buscaría funciones con nombres como _Z1hic
cuando buscaba void h(int, char)
5: Este tipo de mezcla es una razón común para usar extern "C"
, y no veo nada de malo en hacerlo de esta manera, solo asegúrese de entender lo que está haciendo.