Compartir vía


Tutorial: Compilación e importación de unidades de encabezado en Microsoft Visual C++

Este artículo trata sobre la compilación y la importación de unidades de encabezado con Visual Studio 2022. Para obtener información sobre cómo importar encabezados de biblioteca estándar de C++ como unidades de encabezado, consulte Tutorial: Importación de bibliotecas STL como unidades de encabezado. Para obtener una manera aún más rápida y sólida de importar la biblioteca estándar, consulte Tutorial: Importación de la biblioteca estándar de C++ mediante módulos.

Las unidades de encabezado son la alternativa recomendada a los archivos de encabezado precompilado (PCH). Las unidades de encabezado son más fáciles de configurar y usar. Son significativamente más pequeñas en el disco, proporcionan ventajas de rendimiento similares y son más flexibles que una PCH compartida.

Para contrastar las unidades de encabezado con otras formas de incluir funcionalidad en los programas, consulte Comparar unidades de encabezado, módulos y encabezados precompilados.

Requisitos previos

Para usar unidades de encabezado, necesita Visual Studio 2019 16.10 o versiones posteriores.

Definición de unidad de encabezado

Una unidad de encabezado es una representación binaria de un archivo de encabezado. Una unidad de encabezado termina con una extensión .ifc. El mismo formato se usa para los módulos con nombre.

Una diferencia importante entre una unidad de encabezado y un archivo de encabezado es que una unidad de encabezado no se ve afectada por las definiciones de macro fuera de la unidad de encabezado. Es decir, no se puede definir un símbolo de preprocesador que haga que la unidad de encabezado se comporte de forma diferente. En el momento en que importa la unidad de encabezado, esta ya está compilada. Es diferente de cómo se trata un #include archivo. Un archivo incluido puede verse afectado por una definición de macro fuera del archivo de encabezado porque el archivo de encabezado pasa por el preprocesador al compilar el archivo de origen que lo incluye.

Las unidades de encabezado se pueden importar en cualquier orden, que no es true de los archivos de encabezado. El orden del archivo de encabezado es importante porque las definiciones de macro definidas en un archivo de encabezado pueden afectar a un archivo de encabezado posterior. Las definiciones de macros de una unidad de encabezado no pueden afectar a otra unidad de encabezado.

Todo lo que es visible desde un archivo de encabezado también lo es desde una unidad de encabezado, incluidas las macros definidas dentro de la unidad de encabezado.

Un archivo de encabezado se debe traducir a una unidad de encabezado para poder importarlo. Una ventaja de las unidades de encabezado sobre los archivos de encabezado precompilados (PCH) es que se pueden usar en compilaciones distribuidas. Siempre que compile y .ifc el programa que lo importe con el mismo compilador y tenga como destino la misma plataforma y arquitectura, se puede consumir una unidad de encabezado generada en un equipo en otro. A diferencia de un PCH, cuando cambia una unidad de encabezado, solo se vuelve a generar y lo que depende de él. Las unidades de encabezado pueden ser hasta un orden de magnitud menor que un .pch.

Las unidades de encabezado imponen menos restricciones a las similitudes necesarias de las combinaciones de conmutadores del compilador usadas para crear la unidad de encabezado y para compilar el código que lo consume que un PCH. Sin embargo, algunas combinaciones de conmutadores y definiciones de macros pueden crear infracciones de la regla de definición (ODR) entre varias unidades de traducción.

Por último, las unidades de encabezado son más flexibles que un PCH. Con un PCH, no puede elegir incluir solo uno de los encabezados en el PCH, porque el compilador los procesa todos. Con las unidades de encabezado, incluso cuando se compilan juntas en una biblioteca estática, solo se aporta el contenido de la unidad de encabezado que se importa en la aplicación.

Las unidades de encabezado son un paso entre los archivos de encabezado y los módulos de C++20. Proporcionan algunas de las ventajas de los módulos. Son más sólidos porque las definiciones de macros externas no las afectan, por lo que puede importarlas en cualquier orden. Además, el compilador puede procesarlas más rápido que a los archivos de encabezado. Pero las unidades de encabezado no tienen todas las ventajas de los módulos porque las unidades de encabezado exponen las macros definidas en ellos (los módulos no). A diferencia de los módulos, no hay ninguna manera de ocultar la implementación privada en una unidad de encabezado. Para indicar la implementación privada con archivos de encabezado, se emplean técnicas variadas, como agregar caracteres de subrayado iniciales a los nombres o colocar elementos en un espacio de nombres de implementación. Un módulo no expone la implementación privada en ningún formulario, por lo que no es necesario hacerlo.

Considere la posibilidad de reemplazar los encabezados precompilados por unidades de encabezado. Obtiene la misma ventaja de velocidad, pero también con otras ventajas de higiene y flexibilidad del código.

Formas de compilar una unidad de encabezado

Hay varias maneras de compilar un archivo en una unidad de encabezado:

  • Compilar un proyecto de unidad de encabezado compartido. Se recomienda este enfoque porque proporciona más control sobre la organización y reutilización de las unidades de encabezado importadas. Cree un proyecto de biblioteca estática que contenga las unidades de encabezado que desee y, a continuación, haga referencia a él para importar las unidades de encabezado. Para un tutorial de este enfoque, consulte Compilación de un proyecto de biblioteca estática de unidades de encabezado para unidades de encabezado.

  • Elija archivos individuales para traducirlos a unidades de encabezado. Este enfoque proporciona control archivo por archivo sobre lo que se trata como una unidad de encabezado. También es útil cuando debe compilar un archivo como una unidad de encabezado que, como no tiene la extensión predeterminada (.ixx, .cppm, .h, .hpp), no se compilaría normalmente en una unidad de encabezado. Este método se muestra en este tutorial. Para empezar, consulte Enfoque 1: Traducción de un archivo específico a una unidad de encabezado.

  • Buscar y compilar automáticamente unidades de encabezado. Este método funciona, pero es más adecuado para proyectos más pequeños, porque no garantiza un rendimiento de compilación óptimo. Para información detallada sobre este enfoque, consulte Enfoque 2: Buscar automáticamente unidades de encabezado.

  • Como se mencionó en la introducción, puede compilar e importar archivos de encabezado STL como unidades de encabezado y tratar #include automáticamente los encabezados de la biblioteca STL como import sin volver a escribir el código. Para saber cómo hacerlo, visite Tutorial: Importación de bibliotecas STL como unidades de encabezado.

Enfoque 1: Traducción de un archivo específico a una unidad de encabezado

En esta sección, se muestra cómo elegir un archivo específico para traducir a una unidad de encabezado. Siga estos pasos en Visual Studio para compilar un archivo de encabezado como unidad de encabezado:

  1. Cree un proyecto de aplicación de consola de C++.

  2. Reemplace el contenido del archivo de código fuente como se muestra a continuación:

    #include "Pythagorean.h"
    
    int main()
    {
        PrintPythagoreanTriple(2,3);
        return 0;
    }
    
  3. Agregue un archivo de encabezado denominado Pythagorean.h y reemplace el contenido por este código:

    #ifndef PYTHAGOREAN
    #define PYTHAGOREAN
    
    #include <iostream>
    
    inline void PrintPythagoreanTriple(int a, int b)
    {
        std::cout << "Pythagorean triple a:" << a << " b:" << b << " c:" << a*a + b*b << std::endl;
    }
    #endif
    

Configuración de propiedades del proyecto

Para habilitar las unidades de encabezado, establezca primero el estándar /std:c++20 del lenguaje C++ en o posterior con los pasos siguientes:

  1. En el Explorador de soluciones, haga clic con el botón derecho en el nombre del proyecto y elija Propiedades.
  2. En el panel izquierdo de la ventana de páginas de propiedades del proyecto, seleccione Propiedades de configuración>General.
  3. En la lista desplegable Estándar de lenguaje C++, seleccione Estándar ISO C++20 [/std:c++20] o posterior. Elija Aceptar para cerrar el diálogo.

Compile el archivo de encabezado como unidad de encabezado:

  1. En el Explorador de soluciones, seleccione el archivo que quiere compilar como unidad de encabezado (en este caso, Pythagorean.h). Haga clic en el archivo y elija Propiedades.

  2. Establezca el menú desplegable Propiedades de configuración>General>Tipo de elemento en Compilador de C/C++ y elija Aceptar.

    Screenshot that shows changing the item type to C/C++ compiler.

Al compilar este proyecto más adelante en este tutorial, Pythagorean.h se traducirá en una unidad de encabezado. Se traduce en una unidad de encabezado porque el tipo de elemento de este archivo de encabezado se establece en compilador de C/C++ y, dado que la acción predeterminada para .h los archivos y .hpp se establece de esta manera es traducir el archivo en una unidad de encabezado.

Nota:

Esto no es necesario para este tutorial, pero se proporciona como información. Para compilar un archivo como una unidad de encabezado que no tenga una extensión de archivo de unidad de encabezado predeterminada, por .cpp ejemplo, establezca las propiedades>de configuración C/C++>Advanced>Compile As to Compile as to Compile as C++ Header Unit (/exportHeader):Screenshot that shows changing Configuration properties > C/C++ > Advanced > Compile As to Compile as C++ Header Unit (/exportHeader).

Cambiar el código para importar la unidad de encabezado

  1. En el archivo de origen del proyecto de ejemplo, cambie #include "Pythagorean.h" a import "Pythagorean.h"; No olvide el punto y coma final. Es necesario para las import instrucciones. Dado que es un archivo de encabezado de un directorio local para el proyecto, usamos comillas con la import instrucción : import "file";. En sus propios proyectos, para compilar una unidad de encabezado a partir de un encabezado del sistema, use corchetes angulares: import <file>;

  2. Compile la solución mediante Compilar>Compilar solución en el menú principal. Ejecútela para ver que genera la salida esperada: Pythagorean triple a:2 b:3 c:13

En sus propios proyectos, repita este proceso para compilar los archivos de encabezado que quiera importar como unidades de encabezado.

Si solo quiere convertir algunos archivos de encabezado en unidades de encabezado, este es un buen enfoque. Pero si tiene muchos archivos de encabezado que desea compilar y la posible pérdida de rendimiento de la compilación se supera por la comodidad de tener el sistema de compilación controlado automáticamente, consulte la sección siguiente.

Si está interesado en importar específicamente encabezados de biblioteca STL como unidades de encabezado, consulte Tutorial: Importación de bibliotecas STL como unidades de encabezado.

Enfoque 2: Buscar y compilar automáticamente unidades de encabezado

Dado que se tarda tiempo en examinar todos los archivos de origen de las unidades de encabezado y el tiempo para compilarlos, el siguiente enfoque es más adecuado para proyectos más pequeños. No garantiza un rendimiento de compilación óptimo.

Este enfoque combina dos configuraciones de proyecto de Visual Studio:

  • Examinar los orígenes en busca de dependencias de módulo hace que el sistema de compilación llame al compilador para garantizar que todos los módulos importados y las unidades de encabezado se compilen antes de compilar los archivos que dependen de ellos. Cuando se combina con Translate Includes to Imports, los archivos de encabezado incluidos en el origen que también se especifican en un header-units.json archivo ubicado en el mismo directorio que el archivo de encabezado, se compilan en unidades de encabezado.
  • Traducir inclusiones en importaciones trata a un archivo de encabezado como import si #include hace referencia a un archivo de encabezado que se puede compilar como unidad de encabezado (como se especifica en un archivo header-units.json) y hay disponible una unidad de encabezado compilada para el archivo de encabezado. De lo contrario, el archivo de encabezado se trata como #include normal. El header-units.json archivo se usa para compilar automáticamente unidades de encabezado para cada #include, sin duplicación de símbolos.

Puede activar esta configuración en las propiedades del proyecto. Para ello, haga clic en el proyecto en el Explorador de soluciones y elija Propiedades. Luego, elija Propiedades de configuración>C/C++>General.

Screenshot that shows the project properties screen with Configuration highlighted and All Configurations selected. Under C/C++ > General, Scan Sources for Module Dependencies is highlighted and set to yes, and Translate Includes to Imports is highlighted and set to Yes (/translateInclude)

Examinar los orígenes en busca de dependencias de módulo se puede establecer para todos los archivos en el proyecto en Propiedades del proyecto como se muestra aquí o para archivos individuales en Propiedades del archivo. Siempre se examinan los módulos y las unidades de encabezado. Establezca esta opción cuando tenga un archivo .cpp que importe unidades de encabezado que desee compilar automáticamente y que todavía no se haya compilado.

Esta configuración funciona en conjunto para compilar e importar automáticamente unidades de encabezado en estas condiciones:

  • Examinar orígenes para dependencias de módulo examina los orígenes de los archivos y sus dependencias que se pueden tratar como unidades de encabezado. Los archivos que tienen la extensión .ixxy los archivos que tienen sus propiedades>Archivo C/C++>Compile As propiedad establecida en Compilar como unidad de encabezado de C++ (/export) siempre se examinan independientemente de esta configuración. El compilador también busca instrucciones import para identificar dependencias de unidad de encabezado. Si se especifica /translateInclude, el compilador también busca directivas #include que también se especifican en un archivo header-units.json para tratarlas como unidades de encabezado. Se crea un gráfico de dependencias a partir de todos los módulos y las unidades de encabezado en el proyecto.
  • Traducir inclusiones en importaciones Cuando el compilador encuentra una instrucción #include y existe un archivo de unidad de encabezado coincidente (.ifc) para el archivo de encabezado especificado, el compilador importa la unidad de encabezado en lugar de tratar al archivo de encabezado como #include. En combinación con Buscar dependencias, el compilador busca todos los archivos de encabezado que se pueden compilar en unidades de encabezado. El compilador consulta una lista de permitidos para decidir qué archivos de encabezado se pueden compilar en unidades de encabezado. Esta lista se almacena en un archivo header-units.json que debe estar en el mismo directorio que el archivo incluido. Puede ver un ejemplo de un archivo header-units.json en el directorio de instalación de Visual Studio. Por ejemplo, el compilador usa %ProgramFiles%\Microsoft Visual Studio\2022\Enterprise\VC\Tools\MSVC\14.30.30705\include\header-units.json para determinar si un encabezado de la biblioteca de plantillas estándar se puede compilar en una unidad de encabezado. Esta funcionalidad existe para servir como puente con código heredado para obtener algunas ventajas de las unidades de encabezado.

El archivo header-units.json tiene dos fines. Además de especificar qué archivos de encabezado se pueden compilar en unidades de encabezado, minimiza los símbolos duplicados para aumentar el rendimiento de la compilación. Para más información sobre la duplicación de símbolos, consulte Referencia de header-units.json de C++.

Estos modificadores y header-unit.json proporcionan algunas de las ventajas de las unidades de encabezado. Esto es útil, pero conlleva un costo en términos del rendimiento de la compilación. Este método podría no ser el más adecuado para proyectos más grandes, puesto que no garantiza tiempos de compilación óptimos. Además, es posible que los mismos archivos de encabezado se vuelvan a procesar repetidamente, lo que aumentaría el tiempo de compilación. Sin embargo, en función del proyecto, la conveniencia puede valer la pena.

Estas características están diseñadas para código heredado. Si se trata de código nuevo, vaya a módulos en lugar de a unidades de encabezado o archivos #include. Si necesita un tutorial sobre el uso de los módulos, consulte Tutorial de módulos con nombre (C++).

Para un ejemplo de cómo se usa esta técnica para importar archivos de encabezado STL como unidades de encabezado, consulte Tutorial: Importación de bibliotecas STL como unidades de encabezado.

Implicaciones del preprocesador

El preprocesador estándar compatible con C99/C++11 es necesario para crear y usar unidades de encabezado. El compilador habilita al preprocesador compatible con C99/C++11 nuevo cuando se compilan unidades de encabezado al agregar /Zc:preprocessor implícitamente a la línea de comandos cada vez que se usa cualquier forma de /exportHeader. Si se intenta desactivar, se producirá un error de compilación.

Habilitar el preprocesador nuevo afecta el procesamiento de las macros variádicas. Para más información, consulte la sección de comentarios sobre las Macros variádicas.

Consulte también

/translateInclude
/exportHeader
/headerUnit
header-units.json
Comparación de unidades de encabezado, módulos y encabezados precompilados
Información general de los módulos en C++
Tutorial: Importación de la biblioteca estándar de C++ mediante módulos
Tutorial: Importación de bibliotecas STL como unidades de encabezado