Pregunta

Recientemente he publicado una pregunta pidiendo qué acciones constituirían la Zen de C ++ . He recibido respuestas excelentes, pero no podía entender una recomendación:

  • Crear archivos auto-suficiente de cabecera

¿Cómo se asegura sus archivos de cabecera son autosuficientes

Cualquier otro tipo de asesoramiento o de las mejores prácticas relacionadas con el diseño y la implementación de archivos de cabecera en C / C ++ serán bienvenidos.

Edit: He encontrado esta pregunta que se ocupa de las "mejores prácticas" parte de la mía.

¿Fue útil?

Solución

Un archivo de cabecera autosuficiente es uno que no depende del contexto de donde se incluye para funcionar correctamente. Si se asegura de que # include o definir / declarar todo antes de que lo utilice, usted tiene usted mismo un autosuficiente cabecera.
Un ejemplo de un no auto suficiente cabecera 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 utiliza std::string sin primero #including. Para que esto funcione, en MyClass.cpp es necesario poner el #include <string> antes #include "MyClass.h".
Si el usuario de MiClase no lo hace él conseguirá un error que std :: string no está incluido .

El mantenimiento de sus cabeceras para ser autosuficiente puede ser a menudo descuidado. Por ejemplo, usted tiene una gran cabecera MiClase y se agrega a ella otro pequeño método que utiliza std :: string. En todos los lugares de esta clase se utiliza en la actualidad, ya se #include antes MyClass.h. entonces algún día # include MyClass.h como el primer encabezado y de repente tiene todos estos nuevos error en un archivo que ni siquiera toca (MyClass.h)
manteniendo cuidadosamente sus cabeceras para ser autosuficiente ayuda a evitar este problema.

Otros consejos

Goddard Space Flight Center de la NASA (GSFC) ha publicado C y C ++ estándares de programación que se ocupan de este problema.

Suponga que tiene un módulo con una perverse.c archivo de origen y su perverse.h cabecera.

Asegurar una cabecera es autónomo

Hay una forma muy sencilla de garantizar que una cabecera es autónomo. En el archivo de origen, la primera cabecera se incluye es la cabecera del módulo. Si se compila como este, la cabecera es autónomo (autosuficiente). Si no es así, fijar la cabecera hasta que sea (de forma fiable 1 ) autónomo.

perverse.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 ser protegidos contra la inclusión múltiple. (La cabecera <assert.h> estándar es una excepción explícita a la regla -. Por lo tanto el 'casi' calificador)

perverse.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 a pesar de que se considera tradicionalmente una buena idea para incluir las cabeceras estándar antes de las cabeceras del proyecto, en este caso, es crucial para la capacidad de prueba de que el módulo de cabecera (perverse.h) viene antes que todos los demás. La única excepción que me permito se incluye una cabecera de configuración por delante de la cabecera del módulo; Sin embargo, incluso esto es dudoso. Si la cabecera módulo necesita usar (o tal vez sólo 'puede utilizar') la información de la cabecera de configuración, probablemente debería incluir la cabecera de configuración en sí, en lugar de confiar en los archivos de origen de usarlo para hacerlo. Sin embargo, si es necesario configurar la versión de POSIX para solicitar apoyo a, que debe realizarse antes de que se incluyó la primera cabecera del sistema.


Nota 1: Steve Jessop 's a href="https://stackoverflow.com/users/9611/shoosh"> shoosh 's responder es por eso que pongo el paréntesis '(fiable)' comentario en mi 'arreglar' comentario. Dijo:

  

Otro factor que hace que esto sea difícil es las "cabeceras del sistema pueden incluir otras cabeceras" regla en C ++. Si <iostream> incluye <string>, entonces es muy difícil descubrir que se le ha olvidado incluir <string> en alguna cabecera que no [no] usar <iostream> [o <string>]. La compilación de la cabecera por sí da ningún error:. Que es autosuficiente en esta versión de su compilador, pero en otro compilador no podría funcionar

Véase también el responder por Toby Speight sobre IWYU -. Incluir lo que se utiliza


Apéndice: Coincidencia de estas reglas con GCC encabezados precompilados

Las reglas del CCG para encabezados precompilados permiten sólo un ejemplo de cabecera por unidad de traducción, y deben aparecer antes de cualquier fichas C.

GCC 4.4.1 Manual, §3.20 utilizar encabezados precompilados

  

Un archivo de encabezado precompilado se puede utilizar sólo cuando se aplican estas condiciones:

     
      
  • Sólo un encabezado precompilado se puede utilizar en una compilación en particular.
  •   
  • un encabezado precompilado no se puede utilizar una vez que se ve el primer token C. Tu puedes tener   directivas del preprocesador antes de un encabezado precompilado; incluso se puede incluir una precompilado   de cabeza desde dentro de otra cabecera, siempre y cuando no hay fichas C antes de la # include.
  •   
  • [...]
  •   
  • Las macros definidos antes del encabezado precompilado se incluye o bien debe definirse   en la misma forma que cuando se generó el encabezado precompilado, o no debe afectar a la   encabezado precompilado, que por lo general significa que no aparecen en el precompilado   header en absoluto.
  •   

En una primera aproximación, estas limitaciones significan que el encabezado precompilado debe ser el primero en el archivo. Una segunda aproximación señala que si 'config.h sólo contiene declaraciones #define, podría aparecer por delante de la cabecera precompilado, pero es mucho más probable que (a) los define de config.hy afectan al resto del código, y (b) el encabezado precompilado necesita incluir config.h de todos modos.

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

Dados los requisitos divergentes entre las directrices GSFC y encabezados CCG precompilado (y suponiendo que los encabezados precompilados están en uso), creo que iba a garantizar la auto-contención y idempotencia de cabeceras usando un mecanismo separado. Yo ya estoy haciendo esto para los principales proyectos en los que trabajo - la reorganización de las cabeceras para cumplir con las directrices GSFC no es una opción fácil - y la secuencia de comandos que utilizo es chkhdr, se muestra a continuación. Incluso se podría hacer esto como un paso 'construir' en el directorio de cabecera - asegúrese de que todas las cabeceras son autónomos como regla 'compilación'

.

chkhdr guión

Yo uso este script chkhdr para comprobar que los encabezados son autónomos. Aunque el tinglado dice 'Korn', el código es en realidad bien con Bash o incluso el original (Sistema 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

Se da la circunstancia de que nunca he necesitado para pasar cualquier opción que contienen espacios en el guión lo que el código no es sólida en su manejo de opciones de espacios. Manipulación en shell Bourne / Korn al menos hace que el guión más complejo para ningún beneficio; usando Bash y una gran variedad podría ser mejor.

Uso:

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

GSFC estándar disponible a través de Internet Archive

El URL vinculados anterior ya no es funcional (404). Puede encontrar el estándar de C ++ (582-2003-004) en EverySpec.com (en la página 2); el estándar C (582-2000-005) que parece faltar en la acción.

Sin embargo, el estándar de codificación de la NASA C mencionados se puede acceder y descargar a través del archivo de Internet:

http : //web.archive.org/web/20090412090730/http:? //software.gsfc.nasa.gov/assetsbytype.cfm TypeAsset = Estándar

Ver también: ¿Debo usar #include en las cabeceras

Asegúrese de incluir todo lo necesario en la cabecera, en lugar de asumir que algo ha incluido incluye algo más que necesite.

pregunta Viejo, nueva respuesta. : -)

Ahora hay una herramienta llamada incluir-lo-que-uso que está diseñado para analizar su código precisamente para este tipo de problema. En Debian y sistemas derivados, que puede ser instalado como el paquete iwyu.

La idea es que un archivo de cabecera no depende de un archivo de cabecera anterior con el fin de compilar. Por lo tanto el orden de los archivos de cabecera no es significativo. Parte de hacer esto está incluido en un encabezado de archivo todos los otros ficheros de cabecera que va a necesitar. La otra parte es ifdef'ing sus cabeceras para que no se procesan más de una vez.

La idea es que si es necesario agregar un objeto foo a su clase sólo tiene que utilizar #include foo.h y usted no necesita bar.h delante de él con el fin de obtener foo.h para compilar (por ejemplo, hay una llamada en foo que devuelve una instancia de la barra de objetos. no puedes estar interesado en este llamado, pero que tendrá que añadir bar.h dejar que el compilador sabe lo que se está haciendo referencia).

No estoy seguro de que siempre estaría de acuerdo con este consejo. Un gran proyecto tendrá cientos de archivos de cabecera y la compilación va a terminar la lectura a través de los más comunes de los cientos de veces simplemente hacer caso omiso de las #ifdefs. Lo que he visto hacer en este caso es un archivo de cabecera de los ficheros de cabecera que es estándar para el proyecto e incluye los treinta de las más comunes. Siempre es el primero en la lista de los incluye. Esto puede acelerar el tiempo de compilación, pero hace que el mantenimiento de la cabecera en general una tarea especializada.

Lo que quiere utilizar el método descrito en el Manual GNU C preprocesador :

  

2.4 Una vez de sólo encabezados

     

Si un archivo de cabecera pasa a ser incluidos dos veces, el compilador procesará su contenido dos veces. Esto es muy probable que cause un error, por ejemplo, cuando el compilador ve la misma definición de la estructura en dos ocasiones. Incluso si no lo hace, sin duda va a perder el tiempo.

     

La forma habitual de prevenir esto es para encerrar todo el contenido real del archivo en un condicional, como esto:

/* 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 envoltura #ifndef . Cuando la cabecera se incluye de nuevo, el condicional será falsa, porque se define 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 cabecera tiene un ‘#ifndef’ envoltorio. Si un ‘#include’ especifica que la posterior cabecera, y la macro en el ‘#ifndef’ todavía está definido, no se molestó en volver a analizar el archivo en absoluto.

     

Puede poner comentarios fuera de la envoltura. Que no interfieran con esta optimización.

     

El FILE_FOO_SEEN macro se denomina control macro o macro guardia . En un archivo de cabecera de usuario, el nombre de la macro no debe empezar por ‘_’. En un fichero de cabecera del sistema, se debe comenzar con ‘__’ para evitar conflictos con los programas de usuario. En cualquier tipo de archivo de cabecera, el nombre de la macro debe contener el nombre del archivo y un texto adicional, para evitar conflictos con otros archivos de cabecera.

Esta es una gran pregunta. Creo que voy a volver a examinar la práctica de poner un stdafx.h como el primer incluir en cada archivo .cpp al utilizar Visual Studio. Si utiliza archivos de cabecera pre-compilados, no METTER de todos modos, puede ser que también tenga los archivos de cabecera más amigables.

Gracias JALF para su corrección. De Wikipedia

  

Visual C ++ no se compilará nada antes de la # include   "Stdafx.h" en el archivo de origen, a menos   la opción de compilación /Yu'stdafx.h' es   sin marcar (por defecto); que asume toda   código en la fuente hasta e incluyendo   esa línea ya está compilado.

Así que esto significa que los encabezados precompilados infringe la regla cabecera auto-suficiente, ¿verdad?

Al no haber visto a su otra pregunta, mi primer pensamiento en torno a este estaría protegiendo mis archivos de cabecera de varias llamadas (que mis encabezados de valerse por sí mismos).

#ifndef MY_PROTECTED_HEADER_H
#define MY_PROTECTED_HEADER_H
/*
 * Stuff here
 */
#endif /* MY_PROTECTED_HEADER_H */
Licenciado bajo: CC-BY-SA con atribución
No afiliado a StackOverflow
scroll top