¿Por qué necesitamos extern “C” {#include } en C ++?

¿Por qué tenemos que usar:

extern "C" { #include  } 

Específicamente:

  • ¿Cuándo deberíamos usarlo?

  • ¿Qué está sucediendo al nivel de comstackdor / enlazador que nos obliga a usarlo?

  • ¿Cómo en términos de comstackción / vinculación resuelve esto los problemas que requieren que lo usemos?

    C y C ++ son superficialmente similares, pero cada uno comstack en un conjunto de códigos muy diferente. Cuando incluye un archivo de encabezado con un comstackdor C ++, el comstackdor espera el código C ++. Sin embargo, si se trata de un encabezado C, el comstackdor espera que los datos contenidos en el archivo de encabezado se compilen en un determinado formato, el C ++ ‘ABI’ o ‘Application Binary Interface’, por lo que el enlazador se ahoga. Esto es preferible a pasar datos de C ++ a una función que espera datos de C.

    (Para entrar en lo realmente esencial, el ABI de C ++ generalmente “descifra” los nombres de sus funciones / métodos, así que al llamar a printf() sin marcar el prototipo como una función C, C ++ generará llamadas de código _Zprintf , más mierda extra al final.)

    Entonces: use extern "C" {...}; cuando se incluye el encabezado de CA, es así de simple. De lo contrario, tendrá una falta de coincidencia en el código comstackdo y el enlazador se ahogará. Para la mayoría de los encabezados, sin embargo, ni siquiera necesitará el extern porque la mayoría de los encabezados del sistema C ya tendrán en cuenta el hecho de que podrían estar incluidos en el código C ++ y ya extern su código.

    Extern “C” determina cómo deben nombrarse los símbolos en el archivo de objeto generado. Si se declara una función sin extern “C”, el nombre del símbolo en el archivo del objeto usará el cambio de nombre de C ++. Aquí hay un ejemplo.

    Prueba dada. C como tal:

     void foo() { } 

    La comstackción y el listado de símbolos en el archivo de objeto da:

     $ g++ -c test.C $ nm test.o 0000000000000000 T _Z3foov U __gxx_personality_v0 

    La función foo se llama realmente “_Z3foov”. Esta cadena contiene información de tipo para el tipo de retorno y los parámetros, entre otras cosas. Si en su lugar escribes test.C les gusta esto:

     extern "C" { void foo() { } } 

    Luego comstack y mira los símbolos:

     $ g++ -c test.C $ nm test.o U __gxx_personality_v0 0000000000000000 T foo 

    Obtienes un enlace C El nombre de la función “foo” en el archivo de objeto es solo “foo”, y no tiene toda la información de tipo elegante que proviene de la creación de nombres.

    Generalmente, se incluye un encabezado dentro de la opción “C” {} si el código que lo acompaña se compiló con un comstackdor de C pero está intentando llamarlo desde C ++. Cuando haces esto, le estás diciendo al comstackdor que todas las declaraciones en el encabezado usarán el enlace C. Cuando vincula su código, sus archivos .o contendrán referencias a “foo”, no a “_Z3fooblah”, lo que con suerte coincidirá con lo que esté en la biblioteca con la que está enlazando.

    La mayoría de las bibliotecas modernas colocarán guardias alrededor de dichos encabezados para que los símbolos se declaren con el enlace correcto. por ejemplo, en muchos de los encabezados estándar que encontrarás:

     #ifdef __cplusplus extern "C" { #endif ... declarations ... #ifdef __cplusplus } #endif 

    Esto asegura que cuando el código de C ++ incluye el encabezado, los símbolos en su archivo de objeto coinciden con lo que está en la biblioteca de C. Solo debe colocar extern “C” {} alrededor de su encabezado C si es antiguo y ya no tiene estas protecciones.

    En C ++, puede tener diferentes entidades que comparten un nombre. Por ejemplo, aquí hay una lista de funciones llamadas foo :

    • A::foo()
    • B::foo()
    • C::foo(int)
    • C::foo(std::string)

    Para diferenciarlos a todos, el comstackdor de C ++ creará nombres únicos para cada uno en un proceso llamado mangle o decoración. Los comstackdores C no hacen esto. Además, cada comstackdor de C ++ puede hacer esto de otra manera.

    Extern “C” le dice al comstackdor de C ++ que no realice ningún cambio de nombre en el código dentro de los corchetes. Esto le permite llamar a las funciones C desde dentro de C ++.

    Tiene que ver con la forma en que los diferentes comstackdores realizan el cambio de nombre. Un comstackdor de C ++ destruirá el nombre de un símbolo exportado desde el archivo de encabezado de una manera completamente diferente a la de un comstackdor de C, de modo que cuando intente vincularse, obtendrá un error de enlazador que indica que faltan símbolos.

    Para resolver esto, le decimos al comstackdor de C ++ que se ejecute en modo “C”, por lo que realiza el cambio de nombre de la misma forma que el comstackdor de C. Una vez hecho esto, los errores del enlazador son corregidos.

    ¿Cuándo deberíamos usarlo?

    Cuando está enlazando bibliotecas C en archivos de objeto C ++

    ¿Qué está sucediendo al nivel de comstackdor / enlazador que nos obliga a usarlo?

    C y C ++ usan diferentes esquemas para nombrar símbolos. Esto le dice al vinculador que use el esquema de C al enlazar en la biblioteca dada.

    ¿Cómo en términos de comstackción / vinculación resuelve esto los problemas que requieren que lo usemos?

    El uso del esquema de denominación C le permite hacer referencia a los símbolos estilo C. De lo contrario, el enlazador probaría símbolos de estilo C ++ que no funcionarían.

    C y C ++ tienen diferentes reglas sobre los nombres de los símbolos. Los símbolos son cómo el enlazador sabe que la llamada a la función “openBankAccount” en un archivo objeto producido por el comstackdor es una referencia a esa función llamada “openBankAccount” en otro archivo objeto producido desde un archivo fuente diferente por el mismo (o compatible) comstackdor. Esto le permite crear un progtwig a partir de más de un archivo fuente, lo que es un alivio cuando se trabaja en un proyecto grande.

    En C, la regla es muy simple, los símbolos están todos en un solo espacio de nombres de todos modos. Entonces, el entero “calcetines” se almacena como “calcetines” y la función cuenta_calcetines se almacena como “cuellos_contables”.

    Los enlazadores se crearon para C y otros lenguajes como C con esta regla de denominación de símbolos simple. Entonces los símbolos en el enlazador son solo cadenas simples.

    Pero en C ++, el lenguaje te permite tener espacios de nombres y polymorphism y varias otras cosas que entran en conflicto con una regla tan simple. Las seis funciones polimórficas llamadas “agregar” deben tener diferentes símbolos, o la incorrecta será utilizada por otros archivos de objetos. Esto se hace mediante el “mangle” (es un término técnico) los nombres de los símbolos.

    Al vincular el código de C ++ con las bibliotecas o el código de C, necesita todo lo externo “C” escrito en C, como archivos de encabezado para las bibliotecas de C, decirle a su comstackdor de C ++ que estos nombres de símbolos no se deben destruir, mientras que el rest su código C ++, por supuesto, debe ser mutilado o no funcionará.

    Debe usar extern “C” cada vez que incluya un encabezado que defina funciones que residen en un archivo comstackdo por un comstackdor de C, utilizado en un archivo C ++. (Muchas bibliotecas C estándar pueden incluir esta verificación en sus encabezados para que el desarrollador sea más simple)

    Por ejemplo, si tiene un proyecto con 3 archivos, util.c, util.h y main.cpp y los archivos .c y .cpp se comstackn con el comstackdor C ++ (g ++, cc, etc.), entonces no es realmente es necesario, e incluso puede causar errores de enlazador. Si su proceso de comstackción usa un comstackdor de C normal para util.c, necesitará usar extern “C” cuando incluya util.h.

    Lo que sucede es que C ++ codifica los parámetros de la función en su nombre. Así es como funciona la sobrecarga de funciones. Todo lo que tiende a sucederle a una función C es la adición de un guión bajo (“_”) al comienzo del nombre. Sin usar la opción “C”, el vinculador buscará una función llamada DoSomething @@ int @ float () cuando el nombre real de la función sea _DoSomething () o simplemente DoSomething ().

    El uso de la “C” externa resuelve el problema anterior al decirle al comstackdor de C ++ que debe buscar una función que siga la convención de nomenclatura de C en lugar de la de C ++.

    La construcción extern "C" {} ordena al comstackdor que no realice cambios en los nombres declarados dentro de las llaves. Normalmente, el comstackdor de C ++ “mejora” los nombres de las funciones para que codifiquen la información de tipo sobre los argumentos y el valor de retorno; esto se llama el nombre destrozado . El constructo extern "C" previene la destrucción.

    Normalmente se usa cuando el código C ++ necesita llamar a una biblioteca de lenguaje C. También se puede usar al exponer una función de C ++ (desde una DLL, por ejemplo) a clientes C.

    El comstackdor de C ++ crea nombres de símbolos de forma diferente que el comstackdor de C. Por lo tanto, si intenta hacer una llamada a una función que reside en un archivo C, comstackdo como código C, debe decirle al comstackdor de C ++ que los nombres de los símbolos que está tratando de resolver tienen un aspecto diferente al predeterminado; de lo contrario, el paso del enlace fallará.

    Esto se usa para resolver problemas de creación de nombres. Extern C significa que las funciones están en una API de estilo C “plana”.