¿Por qué un compilador no puede evitar importar un archivo de encabezado dos veces por su cuenta?

12

¡Nuevo en C ++! Así que estaba leyendo esto: enlace

  

Protectores de encabezados

     

Debido a que los archivos de encabezado pueden incluir otros archivos de encabezado, es posible   terminar en la situación en la que se incluye un archivo de encabezado múltiple   veces.

Así que hacemos directivas de preprocesador para evitar esto. Pero no estoy seguro: ¿por qué el compilador simplemente ... no importa lo mismo dos veces?

Dado que los guardias de encabezado son opcionales (pero aparentemente son una buena práctica), casi me hace pensar que hay escenarios cuando se quiere importar algo dos veces. Aunque no puedo pensar en ningún escenario semejante. ¿Alguna idea?

    
pregunta Omega 26.03.2013 - 19:36

3 respuestas

27

Pueden, como lo muestran los nuevos idiomas que lo hacen.

Pero hace muchos años se tomó una decisión de diseño (cuando el compilador de C tenía varias etapas independientes) y ahora, para mantener la compatibilidad, el preprocesador debe actuar de cierta manera para asegurarse de que el código antiguo se compile como se espera.

Como C ++ hereda la forma en que procesa los archivos de encabezado de C, mantiene las mismas técnicas. Estamos apoyando una decisión de diseño de edad. Pero cambiar la forma en que funciona es demasiado arriesgado, ya que muchos códigos podrían romperse. Así que ahora tenemos que enseñar a los nuevos usuarios del idioma cómo usar incluir guardias.

Hay un par de trucos con los archivos de encabezado en los que se incluye deliberadamente varias veces (esto realmente proporciona una característica útil). Aunque si rediseñamos el paradigma desde cero, podríamos hacer de esta una forma no predeterminada para incluir archivos.

    
respondido por el Martin York 26.03.2013 - 19:44
7

De lo contrario, no sería tan expresivo, dado que eligieron mantener la compatibilidad con C y, por lo tanto, seguir con un preprocesador en lugar de un sistema de empaque tradicional.

Una cosa que me viene a la mente es que tuve un proyecto que era una API. Tenía dos archivos de encabezado x86lib.h y x86lib_internal.h . Como interno era enorme, segregé los bits "públicos" a x86lib.h para que los usuarios no tuvieran que dedicar tiempo adicional para compilar.

Esto introdujo un problema divertido con las dependencias, así que terminé teniendo un flujo que fue así en x86lib_internal

  1. Establecer la definición del preprocesador interno
  2. Incluya x86lib.h (que fue inteligente para actuar de cierta manera cuando se definió la función interna)
  3. Haga algunas cosas e introduzca algunas cosas que se usan en x86lib.h
  4. Establecer después de que el preprocesador defina
  5. Incluya x86lib.h nuevamente (esta vez ignoraría todo excepto una porción DESPUÉS segregada que dependía de los elementos de x86lib_internal

No diría que era la mejor manera de hacerlo, pero logró lo que quería.

    
respondido por el Earlz 26.03.2013 - 19:51
0

Una dificultad con la exclusión automática de encabezados duplicados es que el estándar C es relativamente silencioso en cuanto al tema de lo que incluye el significado de los nombres de archivo. Por ejemplo, supongamos que el archivo principal que se está compilando contiene las directivas #include "f1.h" y #include "f2.h" , y los archivos encontrados para esas directivas contienen #include "f3.h" . Si f1.h y f2.h están en directorios diferentes, pero se encontraron al buscar incluir rutas, no estaría claro si las directivas #include dentro de esos archivos debían cargar el mismo archivo f3.h , o diferentes.

Las cosas se ponen aún peor si uno agrega las posibilidades de incluir archivos que incluyen rutas relativas. En algunos casos donde los archivos de encabezado utilizan rutas relativas para directivas de inclusión anidadas, y cuando se desea evitar realizar cambios en los archivos de encabezado suministrados, puede ser necesario duplicar un archivo de encabezado en varios lugares dentro de la estructura de directorios de un proyecto. Aunque existen varias copias físicas de ese archivo de encabezado, deben considerarse semánticamente como si fueran un solo archivo.

Si la directiva #pragma once permitiera que un identificador siguiera a once , con la semántica de que el compilador debería omitir el archivo si el identificador coincide con uno de una directiva #pragma once encontrada anteriormente, entonces la semántica sería inequívoca; un compilador que podría decir que una directiva #include cargaría el mismo archivo etiquetado #pragma once que uno anterior, podría ahorrar un poco de tiempo al omitir el archivo sin abrirlo de nuevo, pero tal detección no sería semánticamente importante ya que el archivo se omitirá si el nombre de archivo se reconoce como una coincidencia o no. Sin embargo, no soy consciente de que los compiladores funcionen de esa manera. Hacer que un compilador observe si un archivo coincide con el patrón #ifndef someIdentifier / #define someIdentifier / #endif [for that ifndef] / nothing following y tratar una cosa como equivalente al #pragma once someIdentifier anterior si someIdentifier permanece definido, es esencialmente tan bueno.

    
respondido por el supercat 20.02.2015 - 19:49

Lea otras preguntas en las etiquetas