c++ c gcc clang include-path

c++ - ¿Por qué los proyectos usan el modificador-I incluyen los peligros?



gcc clang (4)

¿Qué razones legítimas existen para -I sobre- -iquote ? -I estandarizado (al menos por POSIX ) mientras que -iquote no lo está. (En la práctica, estoy usando -I porque tinycc (uno de los compiladores con los que quiero que compile mi proyecto) no admite -iquote ).

¿Cómo se gestionan los proyectos con -I los peligros? Tendría los paquetes incluidos en un directorio y usaría -I para agregar el directorio que contiene ese directorio.

  • sistema de archivos: includes/mylib/endian.h
  • línea de comando: -Iincludes
  • Archivo C / C ++: #include "mylib/endian.h" //or <mylib/endian.h>

Con eso, mientras no choques con el nombre de mylib , no lo harás (al menos en lo que concierne a los nombres de encabezado).

Al leer la letra pequeña del conmutador -I en GCC, me sorprende que el uso en la línea de comandos invalide el sistema que incluye. Desde los documentos del preprocesador.

"Puede usar -I para anular un archivo de encabezado del sistema, sustituyendo su propia versión, ya que estos directorios se buscan antes que los directorios de archivos de encabezado estándar del sistema".

No parecen estar mintiendo. En dos sistemas Ubuntu diferentes con GCC 7, si creo un archivo endian.h :

#error "This endian.h shouldn''t be included"

... y luego en el mismo directorio cree un main.cpp (o main.c, la misma diferencia):

#include <stdlib.h> int main() {}

Luego compilando con g++ main.cpp -I. -o main g++ main.cpp -I. -o main (o clang, misma diferencia) me da:

In file included from /usr/include/x86_64-linux-gnu/sys/types.h:194:0, from /usr/include/stdlib.h:394, from /usr/include/c++/7/cstdlib:75, from /usr/include/c++/7/stdlib.h:36, from main.cpp:1: ./endian.h:1:2: error: #error "This endian.h shouldn''t be included"

Así que stdlib.h incluye este archivo types.h, que en la línea 194 simplemente dice #include <endian.h> . Mi aparente error (y quizás el de otros) era que los paréntesis angulares lo habrían evitado, pero -soy más fuerte de lo que pensaba.

Aunque no es lo suficientemente fuerte, porque ni siquiera se puede arreglar al pegar / usr / include en la línea de comando primero, porque:

"Si un directorio de inclusión del sistema estándar, o un directorio especificado con -isystem , también se especifica con -I , se ignora la opción -I . El directorio aún se busca pero como directorio del sistema en su posición normal en la cadena de inclusión del sistema. "

De hecho, la salida detallada para g++ -v main.cpp -I/usr/include -I. -o main g++ -v main.cpp -I/usr/include -I. -o main leaves / usr / include en la parte inferior de la lista:

#include "..." search starts here: #include <...> search starts here: . /usr/include/c++/7 /usr/include/x86_64-linux-gnu/c++/7 /usr/include/c++/7/backward /usr/lib/gcc/x86_64-linux-gnu/7/include /usr/local/include /usr/lib/gcc/x86_64-linux-gnu/7/include-fixed /usr/include/x86_64-linux-gnu /usr/include

Coloréame sorprendido. Supongo que para hacer esto una pregunta:

¿Qué razón legítima hay para que la mayoría de los proyectos lo utilicen? ¿ -I considerando este problema extremadamente grave? Puede anular encabezados arbitrarios en sistemas basados ​​en colisiones incidentales de nombres. ¿No deberían casi todos estar usando -iquote en -iquote lugar?


Esta es tu premisa de que es peligroso. Es falso. El lenguaje abandona la búsqueda de archivos de encabezado con cualquiera de las formas de #include suficientemente definidas por la implementación que no sea seguro usar archivos de encabezado que entren en conflicto con los nombres de los archivos de encabezado estándar. Simplemente abstenerse de hacer esto.


Mirando hacia atrás en los manuales de GCC parece que -iquote y otras opciones solo se agregaron en GCC 4: https://gcc.gnu.org/onlinedocs/gcc-3.4.6/gcc/Directory-Options.html#Directory%20Options

Entonces, el uso de "-I" es probablemente una combinación de: hábito, pereza, compatibilidad con versiones anteriores, ignorancia de las nuevas opciones, compatibilidad con otros compiladores.

La solución es "poner un espacio de nombre" a sus archivos de encabezado colocándolos en subdirectorios. Por ejemplo, ponga su encabezado endian en "include/mylib/endian.h" luego agregue "-Iinclude" a la línea de comandos y puede #include "mylib/endian.h" que no debe entrar en conflicto con otras bibliotecas o bibliotecas del sistema.


Un caso obvio es la compilación cruzada. GCC sufre un poco de la asunción histórica de UNIX de que siempre estás compilando para tu sistema local, o al menos algo que está muy cerca. Es por eso que los archivos de encabezado del compilador están en la raíz del sistema. Falta la interfaz limpia.

En comparación, Windows no asume ningún compilador, y los compiladores de Windows no suponen que está apuntando al sistema local. Es por eso que puede tener un conjunto de compiladores y un conjunto de SDK instalados.

Ahora en compilación cruzada, GCC se comporta mucho más como un compilador para Windows. Ya no asume que pretendes usar los encabezados del sistema local, pero te permite especificar exactamente qué encabezados deseas. Y, obviamente, lo mismo se aplica a las bibliotecas que vinculas.

Ahora tenga en cuenta que cuando haga esto, el conjunto de encabezados de reemplazo está diseñado para ir encima del sistema base. Puede omitir los encabezados en el conjunto de reemplazo si su implementación fuera idéntica. Por ejemplo, es probable que <complex.h> sea ​​el mismo. No hay mucha variación en las implementaciones de números complejos. Sin embargo, no puede reemplazar aleatoriamente los bits de implementación internos como <endian.h> .

TL, DR: esta opción es para personas que saben lo que están haciendo. "Ser inseguro" no es un argumento para el público objetivo.