C ++ #include semántica

Esta es una pregunta múltiple para la misma instrucción de preprocesamiento.

1 – o “”?

Además de la información que se encuentra en MSDN:

#include Directiva (C-C ++)

1.a: ¿Cuáles son las diferencias entre las dos notaciones?
1.b: ¿Todos los comstackdores los implementan de la misma manera?
1.c: ¿Cuándo usarías el , y cuándo usarías el “” (es decir, ¿cuáles son los criterios que usarías para usar uno u otro para incluir un encabezado)?

2 – #include {TheProject / TheHeader.hpp} o {TheHeader.hpp}?

He visto al menos dos formas de escribir, incluida la de los encabezados de proyectos. Teniendo en cuenta que tienes al menos 4 tipos de encabezados, es decir:

  • encabezados privados de su proyecto?
  • encabezados de su proyecto, pero que están exportando símbolos (y por lo tanto, “público”)
  • encabezados de otro proyecto con el que tu módulo se vincula
  • encabezados de un comstackdor o biblioteca estándar

Para cada tipo de encabezados:

2.a: ¿Usarías o “”?
2.b: ¿Incluirías con {TheProject / TheHeader.hpp}, o con {TheHeader.hpp} solamente?

3 – Bonificación

3.a: ¿Trabajas en un proyecto con fonts y / o encabezados dentro de una organización arborescente (es decir, directorios dentro de directorios, en oposición a “cada archivo en un directorio”) y cuáles son los pros / contras?

Después de leer todas las respuestas, así como la documentación del comstackdor, decidí que seguiría el siguiente estándar.

Para todos los archivos, ya sean encabezados de proyectos o encabezados externos, siempre use el patrón:

#include  

El espacio de nombres tiene al menos un directorio profundo para evitar colisiones.

Por supuesto, esto significa que el directorio del proyecto donde se encuentran los encabezados del proyecto también se debe agregar como “encabezado de inclusión predeterminado” al archivo MAKE.

El motivo de esta elección es que encontré la siguiente información:

1. El patrón include “” depende del comstackdor

Daré las respuestas a continuación

1.a El estándar

Fuente:

En la sección 16.2 Inclusión del archivo de origen, podemos leer que:

Una directiva de preprocesamiento de la forma

  #include  new-line 

busca una secuencia de lugares definidos por la implementación para un encabezado identificado únicamente por la secuencia especificada entre los delimitadores , y provoca la sustitución de esa directiva por todo el contenido del encabezado. Cómo se especifican los lugares o el encabezado identificado está definido por la implementación.

Esto significa que #include <...> buscará un archivo de una manera definida de implementación.

Luego, el siguiente párrafo:

Una directiva de preprocesamiento de la forma

  #include "q-char-sequence" new-line 

provoca la sustitución de esa directiva por el contenido completo del archivo fuente identificado por la secuencia especificada entre los “delimitadores.” El archivo fuente nombrado se busca de una manera definida por la implementación. Si esta búsqueda no es compatible, o si la búsqueda falla , la directiva se reprocesa como si se lea

  #include  new-line 

con la secuencia contenida idéntica (incluyendo> caracteres, si corresponde) de la directiva original.

Esto significa que #include “…” buscará un archivo en una implementación definida y luego, si el archivo no se encuentra, hará otra búsqueda como si hubiera sido un #include <...>

La conclusión es que tenemos que leer la documentación de los comstackdores.

Tenga en cuenta que, por algún motivo, en ninguna parte de los estándares la diferencia se establece entre los encabezados “sistema” o “biblioteca” u otros encabezados. La única diferencia parece ser que #include <...> parece apuntar a los encabezados, mientras que #include “…” parece apuntar a la fuente (al menos, en la redacción en inglés).

1.b Visual C ++:

Fuente:

#include “MyFile.hpp”

El preprocesador busca archivos de inclusión en el siguiente orden:

  1. En el mismo directorio que el archivo que contiene la instrucción #include.
  2. En los directorios de cualquier archivo de inclusión abierto previamente en el orden inverso en el que se abrieron. La búsqueda comienza desde el directorio del archivo de inclusión que se abrió por última vez y continúa a través del directorio del archivo de inclusión que se abrió primero.
  3. A lo largo de la ruta especificada por cada opción de comstackdor / I.
  4. (*) A lo largo de las rutas especificadas por la variable de entorno INCLUDE o el entorno de desarrollo predeterminado incluye.

#include

El preprocesador busca archivos de inclusión en el siguiente orden:

  1. A lo largo de la ruta especificada por cada opción de comstackdor / I.
  2. (*) A lo largo de las rutas especificadas por la variable de entorno INCLUDE o el entorno de desarrollo predeterminado incluye.

Nota sobre el último paso

El documento no es claro acerca de la parte “A lo largo de las rutas especificadas por la variable de entorno INCLUDE” para ambos <...> y "..." incluye. La siguiente cita hace que se quede con el estándar:

Para incluir archivos que se especifican como #include “path-spec”, la búsqueda de directorios comienza con el directorio del archivo principal y luego continúa a través de los directorios de los archivos de abuelos. Es decir, la búsqueda comienza en relación con el directorio que contiene el archivo de origen que contiene la directiva #include que se está procesando. Si no hay un archivo de abuelos y no se ha encontrado el archivo, la búsqueda continúa como si el nombre del archivo estuviera entre corchetes angulares.

El último paso (marcado con un asterisco) es una interpretación de la lectura del documento completo.

1.c g ++

Fuente:

La siguiente cita resume el proceso:

GCC […] buscará los encabezados solicitados con #include en [directorios del sistema] […] Todos los directorios nombrados por -I se buscan, en orden de izquierda a derecha, antes que los directorios predeterminados.

GCC busca los encabezados solicitados con #include “archivo” primero en el directorio que contiene el archivo actual, luego en los directorios especificados por -iquote options, luego en los mismos lugares habría buscado un encabezado solicitado con corchetes angulares.

#include “MyFile.hpp”

Esta variante se usa para archivos de encabezado de su propio progtwig. El preprocesador busca archivos de inclusión en el siguiente orden:

  1. En el mismo directorio que el archivo que contiene la instrucción #include.
  2. A lo largo del camino especificado por cada opción del comstackdor de comstackción.
  3. En cuanto a #include

#include

Esta variante se usa para archivos de cabecera del sistema. El preprocesador busca archivos de inclusión en el siguiente orden:

  1. A lo largo de la ruta especificada por cada opción de comstackdor -I.
  2. Dentro de los directorios del sistema.

1.d Oracle / Sun Studio CC

Fuente:

Tenga en cuenta que el texto se contradice de algún modo (vea el ejemplo para comprender). La frase clave es: ” La diferencia es que el directorio actual se busca solo para los archivos de encabezado cuyos nombres ha incluido entre comillas.

#include “MyFile.hpp”

Esta variante se usa para archivos de encabezado de su propio progtwig. El preprocesador busca archivos de inclusión en el siguiente orden:

  1. El directorio actual (es decir, el directorio que contiene el archivo “incluido”)
  2. Los directorios nombrados con las opciones -I, en su caso
  3. El directorio del sistema (por ejemplo, el directorio / usr / include)

#include

Esta variante se usa para archivos de cabecera del sistema. El preprocesador busca archivos de inclusión en el siguiente orden:

  1. Los directorios nombrados con las opciones -I, en su caso
  2. El directorio del sistema (por ejemplo, el directorio / usr / include)

1.e Referencia del comstackdor XL C / C ++ – IBM / AIX

Fuente:

Ambos documentos llevan el título “XL C / C ++ Compiler Reference”. El primer documento es anterior (8.0), pero es más fácil de entender. El segundo es más nuevo (12.1), pero es un poco más difícil de descifrar.

#include “MyFile.hpp”

Esta variante se usa para archivos de encabezado de su propio progtwig. El preprocesador busca archivos de inclusión en el siguiente orden:

  1. El directorio actual (es decir, el directorio que contiene el archivo “incluido”)
  2. Los directorios nombrados con las opciones -I, en su caso
  3. El directorio del sistema (por ejemplo, los directorios / usr / vac [cpp] / include o / usr / include)

#include

Esta variante se usa para archivos de cabecera del sistema. El preprocesador busca archivos de inclusión en el siguiente orden:

  1. Los directorios nombrados con las opciones -I, en su caso
  2. El directorio del sistema (por ejemplo, el directorio / usr / vac [cpp] / include o / usr / include)

1.e Conclusión

El patrón “” podría generar un error de comstackción sutil en los comstackdores, y como actualmente trabajo tanto en Windows Visual C ++, Linux g ++, Oracle / Solaris CC y AIX XL, esto no es aceptable.

De todos modos, la ventaja de las características descritas “” está lejos de ser interesante de todos modos, así que …

2. Use el patrón {namespace} /header.hpp

Vi en el trabajo ( es decir, esto no es teoría, esta es una experiencia profesional dolorosa y de la vida real ) dos encabezados con el mismo nombre, uno en el directorio local del proyecto, y el otro en el global.

Como utilizábamos el patrón “”, y ese archivo se incluía tanto en los encabezados locales como en los encabezados globales, no había forma de entender qué estaba sucediendo realmente, cuando aparecían errores extraños.

Usar el directorio en la inclusión nos habría ahorrado tiempo porque el usuario tendría que escribir:

 #include  

o

 #include  

Notarás que mientras

 #include "Header.hpp" 

habría comstackdo con éxito, por lo tanto, sigue ocultando el problema, mientras que

 #include  

no se habría comstackdo en circunstancias normales.

Por lo tanto, mantener la notación <> habría hecho obligatorio para el desarrollador el prefijo de la inclusión con el directorio correcto, otra razón para preferir <> a “”.

3. Conclusión

Al usar la notación <> y la notación de espacios de nombres juntas, se elimina del precompilador la posibilidad de adivinar los archivos, en lugar de buscar únicamente los directorios de inclusión predeterminados.

Por supuesto, las bibliotecas estándar todavía se incluyen como de costumbre, es decir:

 #include  #include  

Normalmente utilizo <> para encabezados de sistema y “” para encabezados de proyecto. En cuanto a las rutas, eso solo es necesario si el archivo que desea está en un subdirectorio de una ruta de inclusión.

por ejemplo, si necesita un archivo en / usr / include / SDL /, pero solo / usr / include / está en su ruta de inclusión, entonces puede usar:

 #include  

Además, tenga en cuenta que a menos que la ruta que ponga comience con un /, es relativo al directorio de trabajo actual.

EDITAR PARA RESPONDER AL COMENTARIO: Depende, si solo hay unos pocos incluye para una biblioteca, simplemente incluiría su subdirectorio en la ruta de inclusión, pero si la biblioteca tiene muchos encabezados (como docenas), entonces preferiría simplemente tener en un subdirectorio que especifico Un buen ejemplo de esto son los encabezados de sistema de Linux. Los usas como:

 #include  #include  

etc.

EDITAR PARA INCLUIR OTRA RESPUESTA BUENA: también, si es concebible que dos o más bibliotecas proporcionen encabezados con el mismo nombre, entonces la solución de subdirectorios básicamente le da a cada encabezado un espacio de nombre.

Para citar el estándar C99 (de un vistazo, la redacción parece ser idéntica en el estándar C90, pero no puedo cortar y pegar):

Una directiva de preprocesamiento de la forma

# include "q-char-sequence" new-line

provoca la sustitución de esa directiva por el contenido completo del archivo fuente identificado por la secuencia especificada entre los “delimitadores.” El archivo fuente nombrado se busca de una manera definida por la implementación. Si esta búsqueda no es compatible, o si la búsqueda falla , la directiva se reprocesa como si se lea

# include new-line

con la secuencia contenida idéntica (incluyendo> caracteres, si corresponde) de la directiva original.

Por lo tanto, las ubicaciones buscadas por #include "whatever" son un superconjunto de las ubicaciones buscadas por #include . La intención es que el primer estilo se use para encabezados que, en general, “pertenecen” a usted, y el segundo método se usará para encabezados que “pertenecen” al comstackdor / entorno. Por supuesto, a menudo hay algunas áreas grises, ¿qué debería usar para los encabezados de Boost, por ejemplo? #include <> , pero no discutiría demasiado si alguien más en mi equipo quisiera #include "" .

En la práctica, no creo que nadie preste mucha atención a qué forma se utiliza, siempre y cuando la comstackción no se rompa. Ciertamente no recuerdo que alguna vez se mencionara en una revisión del código (o de lo contrario, incluso).

Abordaré la segunda parte de tu pregunta:

Normalmente uso cuando encabezados de un tercero. Y "myHeader.h" cuando se incluyen encabezados dentro del proyecto.

La razón por la que uso lugar de es porque es posible que más de una biblioteca tenga un archivo “libHeader.h”. Para incluirlos, necesita el nombre de la biblioteca como parte del nombre de archivo incluido.

1.a: ¿Cuáles son las diferencias entre las dos notaciones?

“” inicia la búsqueda en el directorio donde se encuentra el archivo C / C ++. <> inicia la búsqueda en directorios -I y en ubicaciones predeterminadas (como / usr / include). Ambos buscan en el mismo conjunto de ubicaciones, solo el orden es diferente.

1.b: ¿Todos los comstackdores los implementan de la misma manera?

Eso espero, pero no estoy seguro.

1.c: ¿Cuándo usarías el <>, y cuándo usarías el “” (es decir, ¿cuáles son los criterios que usarías para usar uno u otro para incluir un encabezado)?

Uso “” cuando se supone que el archivo de inclusión está junto al archivo C, <> en todos los demás casos. En particular, en nuestro proyecto, todos los archivos de inclusión “públicos” están en el directorio de proyecto / inclusión, entonces uso <> para ellos.

2 – #include {TheProject / TheHeader.hpp} o {TheHeader.hpp}?

Como ya se señaló, xxx / filename.h le permite hacer cosas como diskio / ErrorCodes.h y netio / ErrorCodes.h

* encabezados privados de su proyecto?

Encabezado privado de mi subsistema en proyecto. Use “filename.h” Cabecera pública de mi subsistema en el proyecto (no visible fuera del proyecto, pero accesible a otros subsistemas). Use o, dependiendo de la convención adaptada para el proyecto. Prefiero usar

* encabezados de su proyecto, pero que están exportando símbolos (y por lo tanto, “público”)

incluir exactamente como los usuarios de su biblioteca los incluirían. Probablemente

* encabezados de otro proyecto con el que tu módulo se vincula

Determinado por el proyecto, pero ciertamente usando <> * encabezados de un compilador o biblioteca estándar Definitivamente <>, de acuerdo con la norma.

3.a: ¿Trabajas en un proyecto con fonts y / o encabezados dentro de una organización arborescente (es decir, directorios dentro de directorios, en oposición a “cada archivo en un directorio”) y cuáles son los pros / contras?

Trabajo en un proyecto estructurado Tan pronto como tenga más de una veintena de archivos, se hará evidente alguna división. Debes seguir el camino que el código te está llevando.

Si recuerdo bien.

Usas el diamante para todas las bibliotecas que se pueden encontrar en tu “ruta”. Entonces, cualquier biblioteca que esté en el STL, o las que haya instalado. En Linux, su ruta es usualmente “/ usr / include”, en Windows no estoy seguro, pero supongo que está bajo “C: \ windows”.

Usas el “” para especificar todo lo demás. “my_bla.cpp” sin información de directorio de inicio se resolverá en el directorio en el que reside / comstack su código o también puede especificar la ubicación exacta de su inclusión con él. Me gusta esto “c: \ myproj \ some_code.cpp”

El tipo de encabezado no importa, solo la ubicación.

Re <> vs “”. En mi tienda, estoy muy atento en lo que se refiere a “estilo”. Una de las pocas áreas donde tengo un requisito es con el uso de corchetes angulares en las declaraciones #include: la regla es esta: si # incluye un sistema operativo o un archivo compilador, puede usar corchetes angulares si corresponde. En todos los demás casos, están prohibidos. Si # incluye un archivo escrito por alguien aquí o por una biblioteca de un tercero, <> está prohibido.

La razón es esta: #include “xh” y #include no buscan en las mismas rutas. #include solo buscará en la ruta del sistema y lo que sea que haya ingresado. Importantemente, no buscará en la ruta donde se encuentra el archivo xh, si ese directorio no está incluido en la ruta de búsqueda de alguna otra manera.

Por ejemplo, supongamos que tiene los siguientes archivos:

c: \ dev \ angles \ main.cpp

 #include "c:\utils\mylib\mylibrary.h" int main() { return 0; } 

c: \ utils \ mylib \ mylibrary.h

 #ifndef MYLIB_H #define MYLIB_H #include  namespace mylib { void Speak(SpeechType speechType); }; #endif 

c: \ utils \ mhlib \ speech.h

 #ifndef SPEECH_H #define SPEECH_H namespace mylib { enum SpeechType {Bark, Growl}; }; #endif 

Esto no se comstackrá sin cambiar la ruta configurando la variable de entorno PATH o -i’ing en el directorio c: \ utils \ mhlib \. El comstackdor no podrá resvelar #include aunque ese archivo esté en el mismo directorio que mylibrary.h !

Hacemos un uso extenso de las rutas relativas y absolutas en las declaraciones #include en nuestro código, por dos razones.

1) Al mantener las bibliotecas y los componentes fuera del árbol fuente principal (es decir, colocar las bibliotecas de utilidades en un directorio especial), no acoplamos el ciclo de vida de la biblioteca al ciclo de vida de la aplicación. Esto es particularmente importante cuando tiene varios productos distintos que usan bibliotecas comunes.

2) Usamos Junctions para asignar una ubicación física en el disco duro a un directorio en una unidad lógica, y luego usar una ruta totalmente calificada en la unidad lógica en todos los #includes. Por ejemplo:

#include "x:\utils\mylib.h" – bueno, x: es un disco subdividido, y x: \ utils apunta a c: \ code \ utils_1.0 en el disco duro

#include "c:\utils_1.0\mylib.h" – ¡malo! la aplicación tha t # incluye mylib.h ahora está acoplada a una versión específica de la biblioteca MYLIB, y todos los desarrolladores deben tenerla en el mismo directorio en su disco duro, c: \ utils_1.0

Finalmente, un objective amplio pero difícil de lograr de mi equipo es poder respaldar comstackciones de 1 clic. Esto incluye poder comstackr el árbol fuente principal haciendo nada más que obtener código del control fuente y luego presionar ‘comstackr’. En particular, aborrezco tener que establecer rutas y directorios #include en toda la máquina para poder comstackr, porque cada pequeño paso adicional que agregue a la fase de configuración en la construcción de una máquina de desarrollo lo hace más difícil, más fácil de desordenar y lleva más tiempo obtener una nueva máquina para acelerar y generar código.

Hay dos diferencias principales entre <> y "" . El primero es qué personaje terminará el nombre: no hay secuencias de escape en los nombres de los encabezados, por lo que puede verse forzado a hacer #include o "bla>file.cpp" . Probablemente no lo haga aunque la mayoría de las diferencias es que el sistema incluye no se debe producir en "" , simplemente <> . Por lo tanto, no es seguro que #include "iostream" funcione; #include es. Mi preferencia personal es usar "" para archivos que son parte del proyecto, y <> para archivos que no lo son. Algunas personas solo usan <> para encabezados de biblioteca estándar y "" para todo lo demás. Algunas personas incluso usan <> solo para Boost y estándar, depende del proyecto. Como todos los aspectos de estilo, lo más importante es ser coherente.

En cuanto a la ruta, una biblioteca externa especificará la convención para encabezados; por ejemplo . En un proyecto local, escribiría todas las rutas relativas al srcdir de nivel superior (o en un proyecto de biblioteca donde son diferentes, el directorio de inclusión).

Al escribir una biblioteca, también puede ser útil usar <> para diferenciar entre encabezados privados y públicos, o no -I el directorio de origen, pero el directorio anterior, por lo que #include "public_header.hpp" y "src/private_header.hpp" . Depende de ti.

EDITAR: En cuanto a los proyectos con estructuras de directorios, los recomendaría. ¡Imagínese si todos los impulsos estuvieran en un directorio (y no en subespacios)! La estructura del directorio es buena porque le permite encontrar archivos más fácilmente y le permite una mayor flexibilidad en la asignación de nombres ( "module\_text\_processor.hpp" en lugar de "module/text\_processor.hpp" ). Este último es más natural y más fácil de usar.

Utilizo <...> desde el archivo de encabezado del sistema (stdio, iostreams, string, etc.) y “…” para encabezados específicos de ese proyecto.

Usamos #include “header.h” para los encabezados locales del proyecto y #include para el sistema incluye, incluye a terceros y otros proyectos en la solución. Usamos Visual Studio, y es mucho más fácil usar el directorio del proyecto en un encabezado include, de esta manera cada vez que creamos un nuevo proyecto, solo tenemos que especificar la ruta include para el directorio que contiene todos los directorios del proyecto, no una ruta separada para cada proyecto