¿Debo utilizar un único encabezado para incluir todos los encabezados de la biblioteca estática?

StackOverflow https://stackoverflow.com/questions/2226927

  •  19-09-2019
  •  | 
  •  

Pregunta

Tengo una biblioteca estática que estoy construyendo en C++.Lo he separado en muchos archivos de encabezado y fuente.Me pregunto si es mejor incluir todos los encabezados que un cliente de la biblioteca pueda necesitar en un archivo de encabezado que a su vez pueda incluir en su código fuente o simplemente hacer que incluyan solo los encabezados que necesitan.¿Eso hará que el código esté innecesariamente inflado?No estaba seguro de si las clases o funciones que no se utilizan seguirán compiladas en sus productos.

Gracias por cualquier ayuda.

¿Fue útil?

Solución

En general, cuando se enlaza el ejecutable final, se incorporará únicamente los símbolos y funciones que sean realmente utilizadas por el programa. Usted sólo paga por lo que usa. Al menos esa es la forma en la cadena de herramientas GCC parece funcionar para mí. No puedo hablar por todos los cadenas de herramientas.

Si el cliente siempre tendrá que incluir el mismo conjunto de archivos de cabecera, entonces creo que está bien para proporcionar un archivo de cabecera "maestro" que incluye otros, en aras de la conveniencia. Pero hacerlo de modo que el cliente también puede optar por incluir sólo lo que se necesita.

Para reducir los tiempos de compilación en grandes proyectos, es práctica común incluir la menor cantidad de cabeceras como sea posible para hacer una compilación unidad.

Otros consejos

Tenga en cuenta que cada archivo fuente que se compila implica una invocación independiente del compilador. Con cada llamada, el compilador tiene que leer en cada archivo de cabecera incluida, analizar a través de ella, y construir una tabla de símbolos.

Cuando se utiliza uno de estos "incluye los archivos de cabecera mundo" en un montón de archivos de origen, que puede afectar significativamente su tiempo de construcción.

Hay maneras de mitigar esto; por ejemplo, Microsoft tiene una característica encabezado precompilado que salva a cabo esencialmente la tabla de símbolos para las compilaciones posteriores a utilizar.

Hay otra consideración sin embargo. Si voy a usar su clase WhizzoString, que no debería tener cabeceras instaladas para SOAP, OpenGL, y lo que sea. De hecho, prefiero que WhizzoString.h sólo incluyen las cabeceras de los tipos y símbolos que forman parte de la interfaz pública (es decir, las cosas que Estoy va a necesitar como un usuario de su clase).

En la medida de lo posible, se debe tratar de cambiar abarca desde WhizzoString.h a WhizzoString.cpp:

Aceptar:

// Only include the stuff needed for this class
#include "foo.h"  // Foo class
#include "bar.h"  // Bar class

public class WhizzoString
{
    private Foo   m_Foo;
    private Bar * m_pBar;
       .
       .
       .
}

MEJOR:

// Only include the stuff needed by the users of this class
#include "foo.h"  // Foo class

class Bar; // Forward declaration

public class WhizzoString
{
    private Foo   m_Foo;
    private Bar * m_pBar;
       .
       .
       .
}

Si los usuarios de su clase nunca tienen que crear o utilizar un tipo de bar, y la clase no contiene ningún caso de bar, entonces puede ser suficiente para proporcionar solamente una declaración adelantada de Bar en el archivo de cabecera (WhizzoString. CPP tendrá #include "bar.h"). Esto significa que cualquier persona incluyendo WhizzoString.h podría evitar la inclusión de Bar.h y todo lo que incluye.

lo que trata de dar dos opciones:

#include <library.hpp> // include everything
#include <library/module.hpp> // only single module

De esta manera usted no tiene un inmenso archivo de inclusión, y para sus archivos separados, que se apilan ordenadamente en un directorio

Depende de la biblioteca, y cómo se ha estructurado de la misma. Recuerde que los archivos de cabecera para una biblioteca, y que son piezas en las que el archivo de cabecera, son esencialmente parte de la API de la biblioteca. Por lo tanto, si lleva a sus clientes a elegir con cuidado y seleccionar alguno de sus cabeceras, entonces necesitará para apoyar esa disposición durante mucho tiempo. Es bastante frecuente que las bibliotecas para exportar toda su interfaz a través de un archivo, o sólo unos pocos archivos, si una parte de la API es verdaderamente opcional y grandes.

Una consideración debe ser el tiempo de compilación: Si el cliente tiene que incluir dos archivos docena de utilizar su biblioteca, y los que tienen incluye incluye interna, puede aumentar significativamente el tiempo de compilación en un gran proyecto, si se utiliza a menudo. Si usted va esta ruta, asegúrese de que todos sus incluye hayan incluir adecuado guardias en torno no sólo el contenido del archivo, pero la línea que incluye así. Aunque tenga en cuenta: GCC moderna hace un muy buen trabajo de este tema en particular y sólo requiere que los guardias de todo el contenido de la cabecera

.

En cuanto a la hinchazón del programa compilado final, que depende de su cadena de herramientas, y cómo se compiló la biblioteca, no cómo el cliente de la biblioteca incluye archivos de cabecera. (Con la advertencia de que si se declara objetos de datos estáticos en las cabeceras, algunos sistemas terminarán en la vinculación de los objetos que definen esos datos, incluso si el cliente no lo usa.)

En resumen, a menos que sea una gran biblioteca, o una cadena de herramientas muy viejo y de mal humor, yo tiendo a ir con la sola incluyen. Para mí, la congelación de la división de su aplicación actual en los encabezados en la API de la biblioteca es la preocupación más grande que los otros.

El problema con los encabezados de archivos únicos es explicado en detalle por el Dr.dobbs, un escritor experto en compiladores. ¡¡¡NUNCA USE UN SOLO ENCABEZADO DE ARCHIVO!!! Cada vez que se incluye un encabezado en un archivo .cc/.cpp, se debe volver a compilar porque puede alimentar las macros del archivo para alterar el encabezado compilado.Por esta razón, un único archivo de encabezado aumentará drásticamente el tiempo de compilación sin proporcionar ningún beneficio.Con C++ primero debes optimizar el tiempo humano, y el tiempo de compilación es tiempo humano.Nunca debe incluir más de lo que necesita para compilar en cualquier encabezado, ya que aumenta dramáticamente el tiempo de compilación, cada unidad de traducción (TU) debe tener su propio archivo de implementación (.cc/.cpp) y cada TU debe tener nombres de archivo únicos; .

En mi década de experiencia en el desarrollo de SDK de C++, religiosamente SIEMPRE tener tres archivos en CADA módulo.tengo un config.h que se incluye en casi cada archivo de encabezado que contiene requisitos previos para todo el módulo, como platform-config y stdint.h cosa.También tengo un global.h archivo que incluye todos los archivos de encabezado del módulo;este es principalmente para depurar (pista enumera tus costuras en el global.h archivo para un código mejor probado y más fácil de depurar).La pieza clave que falta aquí es que realmente deberías tener una public.h archivo que incluye SOLO su API pública.

En bibliotecas que están mal programadas, como boost y sus horribles nombres de clase lower_snake_case, usan esta peor práctica a medias de usar un detail (a veces llamado 'impl') patrón de diseño de carpetas para "ocultar" su interfaz privada.Hay muchos antecedentes detrás de por qué esta es una peor práctica, pero la historia corta es que crea una LOCO cantidad de escritura redundante que convierte líneas de una sola línea en líneas múltiples, no es compatible con UML y estropea el diagrama de dependencia de UML, lo que resulta en código demasiado complicado y patrones de diseño inconsistentes, como que los niños en realidad sean padres y viceversa.No quieres ni necesitas un detail carpeta, necesita usar una public.h encabezado con un montón de módulos hermanos SIN ESPACIOS DE NOMBRES ADICIONALES donde su detail es un hermano y no un hijo que en realidad es un padre.Se supone que los espacios de nombres son para una cosa y sólo una cosa:para interconectar su código con el código de otras personas, pero si es su código, usted lo controla y debe usar nombres únicos de clase y función porque es una mala práctica usar un espacio de nombres cuando no es necesario porque puede causar una colisión de tabla hash tan lenta. abajo el proceso de compilación.UML es la mejor práctica, por lo que si puede organizar sus encabezados para que sean compatibles con UML, entonces su código será, por definición, más robusto y portátil.A public.h El archivo es todo lo que necesita para exponer solo la API pública;gracias.

Licenciado bajo: CC-BY-SA con atribución
No afiliado a StackOverflow
scroll top