una tipos que programacion otro incluir hacer ficheros crear como cabecera archivos archivo c++ header-files

tipos - Archivos de cabecera autosuficientes en C/C++



que es una cabecera en programacion (8)

Recientemente publiqué una pregunta preguntando qué acciones constituirían el Zen de C ++ . Recibí excelentes respuestas, pero no pude entender una recomendación:

  • Hacer que los archivos de cabecera sean autosuficientes

¿Cómo se asegura de que sus archivos de cabecera sean autosuficientes ?

Cualquier otro consejo o práctica recomendada relacionada con el diseño e implementación de archivos de encabezado en C / C ++ será bienvenida.

Edit: encontré esta pregunta que aborda la parte de "Mejores prácticas" de la mía.


Al no haber visto su otra pregunta, mi primer pensamiento sobre esto sería proteger mis archivos de encabezado de múltiples llamadas (deje que mis encabezados se valgan por sí mismos).

#ifndef MY_PROTECTED_HEADER_H #define MY_PROTECTED_HEADER_H /* * Stuff here */ #endif /* MY_PROTECTED_HEADER_H */

EDITAR: Tenga en cuenta que mi respuesta original fue incorrecta, de ahí la razón por la que podría ver la puntuación negativa en esta respuesta. He corregido mi error original.


Asegúrese de incluir todo lo que necesita en el encabezado, en lugar de suponer que algo que incluyó incluye algo más que necesita.


Desearía utilizar el método descrito en el Manual del preprocesador GNU C :

2.4 Encabezados de una sola vez

Si un archivo de encabezado se incluye dos veces, el compilador procesará su contenido dos veces. Es muy probable que esto cause un error, por ejemplo, cuando el compilador ve dos veces la misma definición de estructura. Incluso si no lo hace, sin duda perderá tiempo.

La forma estándar de evitar esto es encerrar todo el contenido real del archivo en un condicional, como este:

/* File foo. */ #ifndef FILE_FOO_SEEN #define FILE_FOO_SEEN

todo el archivo

#endif /* !FILE_FOO_SEEN */

Esta construcción se conoce comúnmente como un envoltorio #ifndef . Cuando se vuelva a incluir el encabezado, el condicional será falso, porque se definió FILE_FOO_SEEN . El preprocesador omitirá todo el contenido del archivo y el compilador no lo verá dos veces.

CPP optimiza aún más. Se recuerda cuando un archivo de encabezado tiene un envoltorio '' #ifndef ''. Si un '' #include '' posterior especifica ese encabezado, y la macro en '' #ifndef '' aún está definida, no se molestará en volver a escanear el archivo.

Puedes poner comentarios fuera del envoltorio. No interferirán con esta optimización.

La macro FILE_FOO_SEEN se denomina macro de control o macro de guarda . En un archivo de encabezado de usuario, el nombre de la macro no debe comenzar con '' _ ''. En un archivo de encabezado del sistema, debe comenzar con '' __ '' para evitar conflictos con los programas del usuario. En cualquier tipo de archivo de encabezado, el nombre de la macro debe contener el nombre del archivo y un texto adicional, para evitar conflictos con otros archivos de encabezado.


El Centro de Vuelo Espacial Goddard (GSFC) de la NASA ha publicado los estándares de programación C y C ++ que abordan este problema.

Supongamos que tiene un módulo con un archivo de origen perverse.c y su encabezado perverse.h .

Asegurar un encabezado es autónomo

Hay una manera muy simple de asegurar que un encabezado sea autónomo. En el archivo de origen, el primer encabezado que incluye es el encabezado del módulo. Si se compila así, el encabezado es autónomo (autosuficiente). Si no lo hace, arregle el encabezado hasta que sea (de manera confiable 1 ) autocontenido.

perverso.h

#ifndef PERVERSE_H_INCLUDED #define PERVERSE_H_INCLUDED #include <stddef.h> extern size_t perverse(const unsigned char *bytes, size_t nbytes); #endif /* PERVERSE_H_INCLUDED */

Casi todos los encabezados deben estar protegidos contra la inclusión múltiple. (El encabezado estándar <assert.h> es una excepción explícita a la regla, de ahí el calificador "casi").

perverso.c

#include "perverse.h" #include <stdio.h> // defines size_t too size_t perverse(const unsigned char *bytes, size_t nbytes) { ...etc... }

Tenga en cuenta que aunque a menudo es una buena idea incluir los encabezados estándar antes de los encabezados del proyecto, en este caso, es crucial para la verificabilidad que el encabezado del módulo ( perverse.h ) aparezca antes que todos los demás. La única excepción que permitiría es incluir un encabezado de configuración antes del encabezado del módulo; Sin embargo, incluso eso es dudoso. Si el encabezado del módulo necesita usar (o tal vez solo pueda ''usar'') la información del encabezado de configuración, probablemente debería incluir el encabezado de configuración en sí, en lugar de confiar en los archivos de origen que lo utilizan para hacerlo.

Nota al pie 1: El comentario de Steve Jessop a la respuesta de Shoosh es por qué puse el comentario entre paréntesis "(de manera confiable)" en mi comentario "arreglarlo". Él dijo:

Otro factor que dificulta esto es la regla de "los encabezados del sistema pueden incluir otros encabezados" en C ++. Si <iostream> incluye <string> , entonces es bastante difícil descubrir que ha olvidado incluir <string> en algún encabezado que [no] usa <iostream> [o <string> ]. La compilación del encabezado por sí sola no produce errores: es autosuficiente en esta versión de su compilador, pero en otro compilador podría no funcionar.

Apéndice: Emparejar estas reglas con los encabezados precompilados de GCC

Las reglas de GCC para encabezados precompilados permiten solo uno de esos encabezados por unidad de traducción, y debe aparecer antes de cualquier token de C.

GCC 4.4.1 Manual, §3.20 Uso de encabezados precompilados

Un archivo de encabezado precompilado se puede usar solo cuando se aplican estas condiciones:

  • Solo se puede usar un encabezado precompilado en una compilación particular.
  • Un encabezado precompilado no se puede usar una vez que se ve el primer token de C. Puede tener directivas de preprocesador antes de un encabezado precompilado; incluso puede incluir un encabezado precompilado desde dentro de otro encabezado, siempre que no haya tokens C antes de #include.
  • [...]
  • Cualquier macros definida antes de que se incluya el encabezado precompilado debe definirse de la misma manera que cuando se generó el encabezado precompilado, o no debe afectar al encabezado precompilado, lo que generalmente significa que no aparecen en el encabezado precompilado.

Para una primera aproximación, estas restricciones significan que el encabezado precompilado debe ser el primero en el archivo. Una segunda aproximación señala que si ''config.h'' solo contiene #define sentencias, podría aparecer antes del encabezado precompilado, pero es mucho más probable que (a) las definiciones de config.h afecten al resto del código, y (b) el encabezado precompilado debe incluir config.h de todos modos.

Los proyectos en los que trabajo no están configurados para usar encabezados precompilados, y las restricciones definidas por GCC más la anarquía inducida por más de 20 años de mantenimiento intensivo y extensión por una población diversa de programadores significa que sería muy difícil agregarlos .

Dados los requisitos divergentes entre las directrices GSFC y los encabezados precompilados de GCC (y suponiendo que los encabezados precompilados estén en uso), creo que aseguraría la autocontención y la idempotencia de los encabezados mediante un mecanismo separado. Ya hago esto para los principales proyectos en los que trabajo: reorganizar los encabezados para cumplir con las directrices de GSFC no es una opción fácil, y el script que uso es chkhdr , que se muestra a continuación. Incluso podría hacer esto como un paso de "compilación" en el directorio del encabezado; asegúrese de que todos los encabezados sean autocontenidos como una regla de "compilación".

script chkhdr

Utilizo este script chkhdr para comprobar que los encabezados son independientes. Aunque el shebang dice ''Korn shell'', el código está realmente bien con Bash o incluso con el original (System V-ish) Bourne Shell.

#!/bin/ksh # # @(#)$Id: chkhdr.sh,v 1.2 2010/04/24 16:52:59 jleffler Exp $ # # Check whether a header can be compiled standalone tmp=chkhdr-$$ trap ''rm -f $tmp.?; exit 1'' 0 1 2 3 13 15 cat >$tmp.c <<EOF #include HEADER /* Check self-containment */ #include HEADER /* Check idempotency */ int main(void){return 0;} EOF options= for file in "$@" do case "$file" in (-*) options="$options $file";; (*) echo "$file:" gcc $options -DHEADER="/"$file/"" -c $tmp.c ;; esac done rm -f $tmp.? trap 0

Sucede que nunca he necesitado pasar ninguna opción que contenga espacios al script, por lo que el código no es correcto en el manejo de las opciones de espacios. El manejo de ellos en el shell Bourne / Korn al menos hace que el script sea más complejo sin beneficio; el uso de Bash y una matriz podría ser mejor.

Uso:

chkhdr -Wstrict-prototypes -DULTRA_TURBO -I$PROJECT/include header1.h header2.h


Esta es una gran pregunta. Creo que volveré a examinar la práctica de colocar un stdafx.h como la primera inclusión en cada archivo .cpp al usar Visual Studio. Si usa archivos de encabezado precompilados, de todos modos no se mide, podría tener archivos de encabezado más amigables.

Gracias Jalf por la corrección. De Wikipedia

Visual C ++ no compilará nada antes de #include "stdafx.h" en el archivo fuente, a menos que la opción de compilación /Yu''stdafx.h ''esté desactivada (de forma predeterminada); asume que todo el código en la fuente hasta e incluyendo esa línea ya está compilado.

Entonces, esto significa que los encabezados precompilados rompen la regla de encabezado autosuficiente, ¿verdad?


La idea es que un archivo de encabezado no dependa de un archivo de encabezado anterior para compilar. Por lo tanto, el orden de los archivos de encabezado no es significativo. Parte de hacer esto es incluir en un archivo de encabezado todos los demás archivos de encabezado que necesitará. La otra parte es si se definen sus encabezados para que no se procesen más de una vez.

La idea es que si necesita agregar un objeto foo a su clase, solo necesita #incluir foo.h y no necesita poner una barra delante de ella para obtener foo.h para compilar (por ejemplo, hay es una llamada en foo que devuelve una instancia de objeto de barra. Es posible que no esté interesado en esta llamada, pero tendrá que agregar bar.h para que el compilador sepa a qué se hace referencia).

No estoy seguro de estar siempre de acuerdo con este consejo. Un proyecto grande tendrá cientos de archivos de encabezado y la compilación terminará leyendo a través de los más comunes cientos de veces solo para ignorar los #ifdefs. Lo que he visto hacer en este caso es un archivo de encabezado de archivos de encabezado que es estándar para el proyecto e incluye los treinta más comunes. Siempre es el primero en la lista de incluye. Esto puede acelerar el tiempo de compilación, pero hace que el mantenimiento del encabezado general sea una tarea experta.


Un archivo de encabezado autosuficiente es aquel que no depende del contexto en el que se incluye para que funcione correctamente. Si se asegura de #incluir o definir / declarar todo antes de usarlo, tiene un encabezado autosuficiente.
Un ejemplo de un encabezado no autosuficiente podría ser algo como esto:

----- MyClass.h ----- class MyClass { MyClass(std::string s); };

-

---- MyClass.cpp ----- #include <string> #include "MyClass.h" MyClass::MyClass(std::string s) {}

En este ejemplo, MyClass.h usa std :: string sin primero #incluyendo. Para que esto funcione, en MyClass.cpp debe poner el #include antes de #include "MyClass.h".
Si el usuario de MyClass no puede hacer esto, recibirá un error que indica que std :: string no está incluido.

Mantener sus encabezados para que sean autosuficientes a menudo puede ser descuidado. Por ejemplo, tienes un enorme encabezado de MyClass y le agregas otro método pequeño que usa std :: string. En todos los lugares en los que se usa actualmente esta clase, ya está #included antes de MyClass.h. luego, algún día, #include MyClass.h como el primer encabezado y, de repente, tiene todos estos nuevos errores en un archivo que ni siquiera tocó (MyClass.h)
Manteniendo cuidadosamente sus encabezados para que sean autosuficientes en la ayuda para evitar este problema.


Vieja pregunta, nueva respuesta. :-)

Ahora hay una herramienta llamada include-what-you-use que está diseñada para analizar tu código para este tipo de problema. En Debian y sistemas derivados, se puede instalar como el paquete iwyu .