Los módulos ofrecen una alternativa más sencilla, eficiente y segura frente al sistema tradicional de inclusión de archivos mediante el preprocesador.
Tradicionalmente el código se en C++ se ha organizado en ficheros de cabecera .h y ficheros de implementación .cpp. Pero esto, francamente, siempre ha sido un engorro heredado de C 🤷.
La introducción de módulos en C++11 y su formalización en C++20 busca cambiar por completo la forma en que se estructuran y gestionan los proyectos C++.
El concepto de módulo existe en muchos lenguajes, y se ha convertido en un estándar en programación. Supone una enorme mejora para C++.
¿Qué es un módulo?
En términos sencillos, un módulo en C++ es una unidad de código que se compone de una declaración y definición de funciones, clases, todo en el mismo fichero.
Características clave
- Encapsulamiento: Los módulos permiten definir qué es accesible desde fuera (lo que se exporta) y qué es privado para el módulo (lo que se mantiene interno).
- Optimización de la compilación: Los módulos permiten compilar una vez y reutilizar ese resultado, reduciendo los tiempos de compilación.
- Eliminación de dependencias circulares: Los módulos permiten importar solo los componentes necesarios, evitando las dependencias innecesarias.
Si eres un programador habitual, a estas alturas deberías estar llorando de ilusión por ver todas estas ventajas (yo sigo llorando 😭)
Sintaxis Básica de los Módulos
Declarar un Módulo
Un módulo en C++ se declara mediante la palabra clave module seguida del nombre del módulo.
La declaración se coloca generalmente en un archivo separado con extensión .cppm (o a veces .ixx, aunque .cppm es más común y estándar en la mayoría de los compiladores).
module mymodule; // Declaramos un módulo llamado "mymodule"
export void foo() { // Exportamos la función foo
std::cout << "Hello from foo!" << std::endl;
}
export class MyClass { // Exportamos una clase MyClass
public:
void sayHello() {
std::cout << "Hello from MyClass!" << std::endl;
}
};
En este ejemplo:
module mymodule;declara el módulomymodule.- Las funciones y clases que se prefijan con
exportse hacen accesibles desde fuera del módulo.
Importar un Módulo
Para usar un módulo en otro archivo, simplemente usamos la palabra clave import, que reemplaza al tradicional #include.
import mymodule; // Importamos el módulo "mymodule"
int main() {
foo(); // Llamamos a la función exportada desde mymodule
MyClass obj;
obj.sayHello(); // Usamos la clase exportada desde mymodule
}
En este ejemplo,
- El archivo
main.cppimporta el módulomymoduley usa las entidades que han sido exportadas (la funciónfooy la claseMyClass).
Ejemplo con y sin módulos
Vamos a ver las ventajas de los módulos haciendo un ejemplo sencillo con el sistema tradicional de cabeceras, y con el nuevo sistema de módulos.
Beneficios de los Módulos en C++
En gran medida los módulos de C++ están pensados para sustituir el uso de los archivos de encabezado en el lenguaje.
Los módulos introducen una forma más moderna, eficiente y segura de organizar y reutilizar código, solucionando varios problemas asociados con el sistema de encabezados tradicionales.
Por si aún no os he convencido, vamos a ver algunas de sus ventajas 👇
Compilación más rápida
- En el sistema tradicional, cada archivo
.cppdebe incluir todos los encabezados necesarios, lo cual genera una gran cantidad de procesamiento redundante. - Los módulos permiten compilar unidades de código una sola vez y reutilizarlas, lo que disminuye significativamente los tiempos de compilación.
Mejor encapsulamiento
- Los archivos de encabezado tradicionales exponen tanto la interfaz (las declaraciones de clases, funciones, etc.) como algunos detalles de implementación a otros archivos que los incluyan.
- Los módulos definen de manera clara qué partes del código son visibles y accesibles desde otros módulos o archivos, lo que facilita un mejor control sobre la visibilidad y evita dependencias accidentales.
Eliminación de problemas de redefinición
- Los encabezados suelen requerir el uso de
#include guardso#pragma oncepara evitar múltiples inclusiones que causen errores de redefinición. - Los módulos eliminan la necesidad de estos mecanismos, ya que su diseño previene automáticamente la inclusión múltiple y la redefinición. 🎉🎉🎉
Evitación de macros no deseadas
- Las macros en los encabezados pueden generar conflictos cuando se incluyen en diferentes archivos y pueden afectar partes del código de forma no intencional.
- Con los módulos, las macros no se propagan fuera del módulo en el que se definen.
Mejoras en la lectura y mantenimiento
- Los módulos proporcionan un sistema más organizado que facilita la comprensión de las dependencias del proyecto, ya que cada módulo define explícitamente sus interfaces y no depende del uso de directivas de preprocesador como
#include.
