Módulos (C++)

De Wikipedia, la enciclopedia libre

El concepto de programación modular es adoptado por la mayoría de los lenguajes modernos, con la notable excepción de C, y C++ antes de la versión C++20. Estos dos lenguajes usan un preprocesador para incluir encabezados con declaraciones, una técnica que data de la década del 1970, hoy superada universalmente por el uso de módulos.

C++20 incorpora módulos, introduciendo por primera vez una alternativa real a la inclusión de encabezados y que permite evitar totalmente el uso del preprocesador.

Inclusión de encabezados clásica[editar]

C y C++ organizan un proyecto en varios archivos de código, más notablemente de estos dos tipos:

  • código, con extensiones .c o .cpp (entre otras), que se compilan y generan "unidades de traducción"
  • encabezados (headers), con extensiones .h o .hpp (entre otras), que no se compilan y no generan unidades de traducción

Los encabezados no se envían al compilador directamente, sino que son incluidos en archivos de código con la instrucción #include de preprocesador como ésta:

#include <stdio.h>

El preprocesador copia y pega el contenido del archivo encabezado en el código justo antes de compilar (el resultado del preprocesamiento se compila pero no se guarda). Algunas consecuencias indeseadas:[1]

  • el compilador vuelve a procesar el código del mismo encabezado por cada vez que se incluya en diferentes archivos de código, provocando un retrabajo inútil
  • por lo anterior y para satisfacer la regla de definición única, el archivo encabezado que será procesado varias veces no puede tener la definición de sus funciones, sino sólo su declaración, lo que obliga a separar el código en un archivo encabezado y otro de código
  • para evitar las redeclaraciones, los encabezados deben usar guardas

Módulos en C++[editar]

Los módulos incorporados en C++20 presentan una alternativa a la inclusión de encabezados[2]​ que evita las consecuencias indeseadas mencionadas y mejora la expresividad y modularidad del código. Si bien los módulos resuelven estos problemas, no fueron planteados como una mera alternativa a la inclusión de encabezado sino como una nueva capacidad de C++. Consecuencia de esto, los módulos no pueden desplazar completamente la inclusión de encabezados existentes, aunque sí pueden evitarla completamente para nuevas bibliotecas que aprovechen la nueva capacidad de módulos.

En C++20 un módulo[3]​ es una unidad de traducción con dos particulares relevantes:

  • tiene un nombre de módulo declarado en el propio código
  • es capaz de exportar símbolos (funciones, clases, etc.)

A continuación se ilustra el código de un módulo elemental:

export module mi_modulo;  // nombra el módulo que se define en este archivo
export int suma(int a, int b){return a+b;}  // Sólo los elementos exportados serán accesibles or

Un archivo de código que requiera utilizar las definiciones exportadas de un módulo sólo tiene que indicarlo con la instrucción:

import mi_modulo;

Cabe notar que, a diferencia de #include, esta forma de import no necesita indicar un nombre de archivo, sino un nombre de símbolo, específicamente un nombre de módulo, que es una entidad nueva en el lenguaje. El linker buscará ese nombre entre las unidades de traducción.

Esta forma de programación, muy natural para los programadores, requiere cambios profundos en el compilador, que debe ser capaz de averiguar símbolos ajenos al archivo de código que está compilando.

Fragmentos globales y privados[editar]

Para mayor flexibilidad C++20 permite que un módulo se desarrolle en varios archivos de código, a los que denomina fragmentos. Incluso permite manejar el nivel de visibilidad de los símbolos exportados como globales o privados, según sean accesibles por todos o solamente por otros fragmentos del mismo módulo.

Reemplazo de la inclusión de encabezados[editar]

La instrucción import tiene una sintaxis alternativa similar a #include:

import <iostream>;

En este caso import no nombra un módulo sino un archivo de encabezados, y lo procesa como tal. A priori parece una mera alternativa sintáctica al clásico #include, pero opera de manera diferente y más eficiente, y no es totalmente compatible: todos los headers estándar se han reescrito para ser importables, pero la manera de escribir headers propios para que sean importables depende del compilador.[4]

No cabe esperar que la inclusión de encabezados de bibliotecas existentes sea reemplazada por módulos, pero sí se espera que nuevas bibliotecas se escriban en términos de módulos en lugar de encabezados, y que la directiva #include vaya perdiendo protagonismo aunque no se vislumbra que se vuelva obsoleta.

Referencias[editar]

  1. «Modules — Clang 12 documentation». clang.llvm.org. Consultado el 26 de octubre de 2020. 
  2. Nayar, Amit. «C++20 in 2020: Modules | Inside PSPDFKit». PSPDFKit (en inglés). Consultado el 26 de octubre de 2020. 
  3. «Understanding C++ Modules: Part 1: Hello Modules, and Module Units». vector-of-bool.github.io (en inglés). 10 de marzo de 2019. Consultado el 26 de octubre de 2020. 
  4. «C++20: An (Almost) Complete Overview - Marc Gregoire - CppCon 2020». 

Enlaces externos[editar]