C++

por Will Buik

¡La compatibilidad con el módulo C ++ ha llegado a Visual Studio! Toma la última versión preliminar de Visual Studio si quieres probarla. Los módulos C ++ pueden ayudarlo a compartimentar su código, acelerar los tiempos de compilación y funcionan a la perfección, en paralelo con su código existente.

Esta vista previa solo admite módulos C ++ en el IDE para proyectos de MSBuild. Si bien el conjunto de herramientas de MSVC es compatible con cualquier sistema de compilación, el soporte IDE de Visual Studio para CMake aún no es compatible con los módulos C ++. ¡Te avisaremos una vez que lo esté! Como siempre, pruébelo y háganos saber si tiene algún comentario.

C++
C++

Conceptos básicos del módulo

Los módulos C ++ le permiten controlar de cerca lo que está disponible para las unidades de traducción que los consumen. A diferencia de los encabezados, no filtrarán definiciones de macros ni detalles de implementación privados (no se necesitan prefijos ridículos). Además, a diferencia de los encabezados, se crean una vez y luego se pueden consumir muchas veces en sus proyectos, lo que reduce los gastos generales de construcción.

C ++ 20 introduce nuevas palabras clave para definir y consumir módulos y Visual Studio usa un nuevo tipo de archivo “.ixx” para definir la interfaz de un módulo. Siga leyendo para conocer los detalles.

Introducción a los módulos en Visual Studio

Si creó un proyecto nuevo en la última vista previa, no necesita hacer nada. Sin embargo, antes de que pueda agregar o consumir módulos en proyectos existentes, debe asegurarse de que está utilizando el último estándar de lenguaje C ++.

Para hacer esto, configure el estándar del lenguaje C ++ en “Preview / std: c ++ latest”. Si tiene varios proyectos en su solución, recuerde hacer esto para todos ellos.

General> Estándar del lenguaje C ++, establecido en "Vista previa / estándar: c ++ más reciente"

¡Y eso es! Está listo para usar módulos C ++ con Visual Studio.

Crear módulos

Para agregar un módulo a un proyecto, deberá crear una interfaz de módulo. Estos son archivos fuente normales de C ++ con la extensión “.ixx”. Pueden incluir encabezados, importar otros módulos e incluirán las definiciones exportadas de su módulo. Puede agregar tantos de estos a un proyecto como desee.

Agregar nuevo elemento> "Unidad de interfaz del módulo C ++ (.ixx)"

Así es como se ve esto en el Explorador de soluciones. En este ejemplo, los proyectos fibprinterdefinen módulos C ++.

Explorador de soluciones con tres interfaces de módulo

Nota: Si bien este ejemplo muestra todas las interfaces de módulo en archivos “.ixx”, cualquier archivo fuente de C ++ puede tratarse como una interfaz de módulo. Para hacer esto, establezca la propiedad “Compilar como” en un archivo fuente en “Compilar como módulo”. La propiedad “Compilar como” se puede encontrar en la pestaña “Avanzado” en la página de propiedades de cualquier archivo de origen.

Exportación de módulos

Entonces, ¿qué entra realmente en una interfaz de módulo? El siguiente ejemplo define un módulo simple llamado DefaultPrintery exporta una sola estructura:

module; //begins global module fragment 
#include <iostream> 
export module DefaultPrinter; 
export struct DefaultPrinter 
{ 
     void print_element(int e) 
     { 
         std::cout << e << " ";
     } 
     void print_separator() 
     { 
         std::cout << ", "; 
     } 
     void print_eol() 
     {    
         std::cout << '\n'; 
     } 
};

Para desglosar un poco el ejemplo, puede ver la nueva sintaxis de exportación en las líneas 1, 5 y 7. La línea 1 especifica que se trata de una interfaz de módulo. La línea 5 define y exporta el módulo en sí y la línea 7 exporta una estructura. Cada módulo puede exportar muchos elementos, como estructuras, clases, funciones y plantillas.

Las interfaces del módulo pueden incluir encabezados e importar otros módulos. Cuando se importan, no filtrarán ningún detalle de estos encabezados o módulos incluidos a menos que los importe explícitamente. Este aislamiento puede ayudar a evitar colisiones de nombres y filtrar detalles de implementación. También puede definir macros de forma segura y utilizar espacios de nombres en las interfaces del módulo. No se filtrarán como los encabezados tradicionales.

Para los  #includeencabezados en una interfaz de módulo, asegúrese de colocarlos en el  fragmento de módulo global entre  y  .module;export module mymodule;

Este ejemplo coloca la implementación en la interfaz del módulo, pero eso es opcional. Si mira hacia atrás en el explorador de soluciones antes de que pueda ver que la interfaz fibgen.ixx tiene una implementación correspondiente en fibgen.cpp.

Su interfaz se ve así:

export module FibGenerator; 
export fib gen_fib(int start, int &len);

Con una implementación correspondiente:

module FibGenerator; 
fib gen_fib(int start, int &len) 
{     
    //... 
}

Aquí, la interfaz define el nombre del módulo y exporta gen_fib. La implementación correspondiente usa la modulepalabra clave para definir a qué módulo pertenece la implementación para que todo se pueda combinar en una unidad cohesiva automáticamente en el momento de la construcción.

Consumir módulos

Para consumir módulos, use la nueva importpalabra clave.

module; 
#include <ranges> 
#include <concepts> 

import DefaultPrinter; 

struct DefaultFormatter 
{ 
     template<is_series S, is_printer T> 
     void format(T t, S s) 
     { 
          while (!s.done()) 
          { 
               t.print_element(s.next()); 
               t.print_separator(); 
           } 
           t.print_eol(); 
      } 
};

Todos los elementos exportados desde la interfaz del módulo estarán disponibles para su uso. Este ejemplo hace uso del DefaultPrintermódulo del primer ejemplo, importándolo en la línea 5.

Su código puede consumir módulos en el mismo proyecto o cualquiera a los que se haga referencia automáticamente (usando referencias de proyecto a proyecto para proyectos de biblioteca estática).

Consumir módulos de otros módulos

También puede importar módulos desde otra interfaz de módulo. A continuación, se muestra un ejemplo que amplía el DefaultPrintermódulo anterior:

module; 
#include <iostream> 
import DefaultPrinter; 

export module TabbedPrinter; 

export struct TabbedPrinter : DefaultPrinter 
{ 
     void print_separator() 
     { 
           std::cout << "\t"; 
     } 
};

Este ejemplo importa el DefaultPrintermódulo anterior y anula su print_separatorfunción. Otro código ahora puede importar esto TabbedPrintersin necesidad de preocuparse por los detalles de DefaultPrinter. Visual Studio se asegurará de que todo esté construido en el orden correcto.

Módulos externos

También es posible hacer referencia a módulos que existen en el disco, en lugar de a los que pertenecen a otro proyecto en la solución. Sin embargo, se debe tener cuidado aquí, porque los módulos son archivos binarios compilados. Debes asegurarte de que sean compatibles con la forma en que estás construyendo tus proyectos.

Puede decirle a Visual Studio que busque módulos en el disco editando la propiedad Dependencias de módulo adicional:

C / C ++> General> Dependencias de módulos adicionales, puede agregar módulos con "/ referencia [[nombre_módulo] =] ruta", varios módulos se pueden separar por punto y coma

IntelliSense y módulos

Todas las funciones de IntelliSense que conoce y ama también funcionan con módulos. Las funciones como finalización de código, ayuda de parámetros, Buscar todas las referencias, Ir a definición y declaración, cambiar el nombre y más, funcionan en todas las soluciones de la manera que esperaría cuando usa módulos.

Aquí puede ver Buscar todas las referencias y Peek Definition trabajando con nuestro TabbedPrintermódulo anterior. Por ejemplo, puede mostrar todas las referencias de la DefaultPrinter estructura exportada desde el DefaultPrinter módulo y mostrar su definición:

Buscar todas las referencias 

Find All References of the “DefaultPrinter” structure across the solution

Definición de Peek 

Peek Definition of the “DefaultPrinter” structure

También puede ir a o echar un vistazo a la definición de un módulo desde cualquier lugar que lo importe:

Peek Definición de un módulo importado, "DefaultPrinter"

Vea los módulos en acción

Para ver todo esto en acción, consulte nuestra demostración de módulos de CppCon 2020 . También hay muchas otras demostraciones de las últimas funciones de Visual Studio y C ++ 20 en acción, si está interesado.

Header Units

Las unidades de encabezado le permiten tratar un encabezado estándar como un módulo. Puede importar una unidad de encabezado utilizando la sintaxis import "header.h"; or import <header>. Las unidades de encabezado se tratan como módulos generados automáticamente. Todos los elementos declarados en el encabezado (y sus inclusiones) se exportan automáticamente. Como un módulo, las definiciones de preprocesador definidas en el código que importa una unidad de encabezado se aislarán y no influirán en la unidad de encabezado importada de ninguna manera. Sin embargo, a diferencia de un módulo, cualquiera estará disponible para su uso en su código cuando importe una unidad de encabezado.

Próximamente se ofrecerá compatibilidad completa con IDE y conjuntos de herramientas para unidades de encabezado. Puede rastrear el estado del soporte de la unidad de encabezado para Microsoft STL aquí en GitHub .

Retroalimentación

Si está interesado en probar módulos C ++ con su propio código, le insto a que obtenga la última versión preliminar de Visual Studio. Pruébelo y avísele si tiene alguna pregunta o comentario. Si encuentra algún problema o tiene una sugerencia, la mejor manera de comunicarse con nosotros es informar  un problema.

Fuente: https://devblogs.microsoft.com/cppblog/a-tour-of-cpp-modules-in-visual-studio/

Deja un comentario