C++ News: La evolución de C++ en el período 2006-2020

La biblioteca digital de la ACM (Association for Computing Machinery) publicó en junio de 2020 un extenso artículo de Bjarne Stroustrup acerca de la evolución reciente del lenguaje C++. La publicación cubre el período comprendido entre 2006 y 2020 y, por tanto, relata los cambios fundamentales introducidos en el estándar ISO del lenguaje durante sus actualizaciones de 2011, 2014, 2017 y 2020.

El artículo incide en la necesidad de preservar los principios fundacionales clave de C++ ante un número cada vez mayor de propuestas de estandarización, así como en el esfuerzo continuo por simplificar su uso a la vez que se mejora su seguridad y se garantiza su retrocompatibilidad. Se discuten también los últimos avances en programación genérica, programación en tiempo de compilación, concurrencia y paralelismo, entre otros muchos aspectos del lenguaje.

Se trata, sin duda, de un texto de obligada lectura para cualquier programador interesado en la evolución histórica de C++ y, más en general, de los lenguajes de programación. El artículo puede encontrarse en la siguiente dirección:
  • Bjarne Stroustrup - Thriving in a crowded and changing world: C++ 2006–2020 - https://dl.acm.org/doi/10.1145/3386320

Post actualizado: Simplificando el empleo de std::cin

El artículo 'Simplificando el empleo de std::cin' publicado originalmente en febrero de 2019 ha sido reescrito de forma sustancial, proporcionando una pequeña biblioteca para C++20 que facilita la adquisición de datos desde la terminal. Puede accederse a él pinchando sobre el enlace anterior.

El post actualizado incluye la implementación de tres funciones genéricas:

  • terminal::prompt: Imprime un mensaje en la terminal y aguarda a que el usuario introduzca el dato solicitado. Si el input es válido, éste es almacenado en una variable capturada por referencia. En caso contrario, vuelve a solicitarse el dato. Opcionalmente, es posible proporcionar una condición que deba ser satisfecha por el input.
  • terminal::prompt_init: Una función auxiliar similar a la anterior que inicializa ella misma por defecto una variable del tipo a leer y procede a su lectura por la terminal.
  • terminal::prompt_loop: Extiende la lista de argumentos de prompt() con una palabra clave centinela configurable por el programador, capaz de interrumpir bucles for y while.
A modo de ejemplo:

   auto d = 0.0;    terminal::prompt("Insert a double > 0.0: ", d, [](auto a){ return a > 0.0; });    auto const s = terminal::prompt_init<string>("Insert a non-empty string: ",                                                 [](auto const& w){ return !w.empty(); });

Programando con C++20 (Parte III): Funciones std::erase y std::erase_if

Artículos de la serie:

Introducción

Consideremos el problema de eliminar de un contenedor estándar todos aquellos elementos que cumplan un criterio específico, bien porque éstos se comparen igual a un cierto valor dado o porque cumplan un determinado predicado unario. De emplear estándares previos a C++20, la consecución de una acción de este tipo en contenedores como std::vector, std::string o std::deque requeriría el uso de la solución idiomática conocida bajo el nombre de erase-remove [1]. En contraste, listas como std::list o std::forward_list permitirían usar funciones miembro públicas de eliminación específicas.

Novedades del blog - Octubre de 2020

Durante los últimos tres meses, el blog ha visto modificado su diseño general, adoptando un estilo de presentación más homogéneo y minimalista.

Asimismo, se han revisado numerosas entradas --particularmente las correspondientes al período 2015-2018-- con el fin de remaquetar sus códigos. La práctica totalidad de los artículos se encuentra ahora adaptada a las técnicas de programación propias de C++17 y/o C++20, empleando de forma consistente el estilo always auto.

La edición de los códigos se ha llevado a cabo en el entorno Visual Studio Code con el tema de color 'Dark (Visual Studio)', cuyo código fuente está disponible en el portal https://github.com/Microsoft/vscode bajo licencia MIT. Los ejemplos han sido puestos a prueba con el compilador GCC 9.3 o superior (https://gcc.gnu.org/).

Por supuesto, los posts seguirán siendo sometidos a revisiones periódicas con el objetivo de mejorar sus contenidos y corregir posibles erratas. El carácter divulgativo y sin ánimo de lucro del blog permanece, asimismo, como una de sus señas de identidad.

Programando con C++20 (Parte II): Lambdas 'templatizadas'

Artículos de la serie:

Introducción


En C++20, las expresiones lambda genéricas pueden adoptar una sintaxis análoga a la de las plantillas de función (function templates) tradicionales [1]:
   [capture_list] <template_parameter_list>(optional C++20)  (parameters)(optional)    mutable-constexpr-consteval(optional)  noexcept(optional)       -> return_type(optional)       requires(optional C++20)    { body }

En la especificación general anterior, template_parameter_list es una lista no vacía de parámetros de plantilla --expresada entre llaves angulares-- que podemos emplear de forma opcional para dotar de nombres específicos a los parámetros de una expresión lambda genérica. Su inclusión vendrá acompañada típicamente por el uso de conceptos que impongan ligaduras sobre los tipos (cláusula requires).

Gracias a esta sintaxis, podemos requerir, por ejemplo, que varios argumentos de una lambda sean del mismo tipo genérico (en el código inferior, el tipo I se encuentra restringido por el concepto std::input_iterator):

   auto f = []<std::input_iterator I>(I first, I last) { /* ... */ };

Como ejemplo adicional, la siguiente lambda genérica opera únicamente con contenedores de tipo std::array:

   auto g = []<typename T, std::size_t N>(std::array<T, N>& m) { /* ... */ };

Referencias de reenvío y auto&&

Artículos de la serie:

Reglas de deducción de tipos


Dada una plantilla de función, una referencia de reenvío es una referencia rvalue a uno de los parámetros de la plantilla. Dicha referencia debe carecer, además, de calificadores const-volatile. A modo de ejemplo, consideremos la plantilla de función f siguiente:

template<typename T> void f(T&& t); // referencia de reenvío en la lista de parámetros de la función

Programando con C++20 (Parte I): Concepts

Artículos de la serie:

Introducción

En este post consideraremos el problema de codificar el bien conocido algoritmo de ordenación por inserción mediante técnicas propias de programación genérica, de manera que éste pueda ser empleado con contenedores diversos. Analizaremos su implementación a la luz de la inclusión de conceptos (concepts), rangos y proyecciones en el estándar del lenguaje C++20 [1-4].

Un concepto es un predicado con nombre, evaluado en tiempo de compilación, que forma parte de la interfaz de una plantilla de clase o de función (template) y restringe el tipo de datos con los que ésta puede operar. La detección temprana de cualquier incumplimiento de tales restricciones, durante los primeros pasos en la generación de una instancia de la plantilla, conducirá por lo general a mensajes de error de fácil comprensión y rastreo. Más que meras ligaduras sintácticas, el objetivo es aquí el de modelar auténticas categorías semánticas que puedan ser reutilizadas por el programador.

Integrando VSCode, Mingw-w64 y CMake (Parte V)

Artículos de la serie:

Ejemplo de árbol de directorios gestionado por CMake


En esta continuación de la serie de artículos dedicada a la integración de VSCode, Mingw-w64 y CMake en MS Windows, analizaremos un nuevo ejemplo de construcción de proyectos mediante CMake moderno.

Como sabemos, CMake emplea ficheros de configuración CMakeLists.txt para producir archivos de construcción específicos que puedan ser utilizados por la herramienta de construcción nativa de nuestra plataforma (Make o Ninja, por ejemplo).

En un post anterior contemplábamos un caso de construcción extremadamente sencillo, consistente en la mera compilación de un único fichero fuente .cpp con dependencias a bibliotecas externas. Como segundo ejemplo, consideraremos ahora el diseño de un proyecto que involucre la codificación de varias bibliotecas internas y un ejecutable. Nuestra estructura de directorios tomará la forma siguiente (véase también la figura superior):

C++ News: ¿Dónde poder obtener información actualizada sobre el lenguaje?

Aun cuando abunden las fuentes de información sobre C++, un porcentaje nada despreciable de ellas proporciona contenidos que, desde una perspectiva actual, consideraríamos obsoletos. Así ocurre con multitud de referencias bibliográficas centradas en técnicas de programación propias de C++98 que, si bien perfectamente válidas en su momento, puedan haberse visto superadas con los estándares C++11 y posteriores. En el peor de los casos, algunas referencias emplean aún un estilo propio de 'C con clases', presentando C++ como un mero superconjunto de C con herramientas añadidas de programación orientada a objetos.

C++ News: GCC 10.1

El pasado 7 de mayo fue publicada la nueva versión 10.1 de la colección de compiladores GCC, la cual implementa múltiples características del nuevo estándar ISO C++ 2020. Entre ellas, destacaríamos:
  • Conceptos, incluyendo la sintaxis simplificada para plantillas de función con ligaduras.
  • Structure bindings extendidos.
  • Atributo [[nodiscard("with reason")]] con exposición de motivo.
  • Inicialización de agregados con paréntesis, permitiendo su uso combinado con funciones como std::make_unique y std::make_shared.
  • CTAD para agregados.
  • Corrutinas (bajo -fcoroutines).
Asimismo, a nivel de biblioteca estándar, el compilador proporciona, entre otras funcionalidades:

Programación basada en contratos (II)

Introducción


Diagrama basado en commons.wikimedia.org/wiki/File:Design_by_contract.svg (CC0 1.0)
Un contrato es un conjunto de precondiciones, postcondiciones y aserciones (todas ellas predicados) asociadas a una función. En concreto [1]:
  • Las precondiciones establecen los requisitos a verificar por los argumentos de una función (y/o el estado de otros objetos de la que dependa) en el punto de entrada de su ejecución. La responsabilidad de su cumplimiento recae en el cliente de la función (caller).
  • Las postcondiciones fijan los requisitos a cumplir por los valores retornados por la función (y/o el estado de otros objetos) al finalizar su ejecución. Son una garantía ofrecida por la función (callee) al cliente.
  • Las aserciones, por su parte, establecen condiciones que deban satisfacerse en puntos específicos del cuerpo de la función. Suelen emplearse para verificar el mantenimiento de los invariantes de una clase.

Programación basada en contratos (I)

Introducción


Un programador de C++ debe lidiar continuamente con múltiples desafíos: garantizar una correcta recuperación de los programas ante excepciones, prevenir bugs tales como accesos fuera de rango o referencias colgantes, adoptar estrategias ante un posible agotamiento de la memoria y un largo etcétera.  En este post trataremos de arrojar algo de luz sobre cómo manejar tales situaciones de forma correcta. No en vano, una de las características definitorias de cualquier lenguaje de programación es, precisamente, el conjunto de herramientas que pone a nuestra disposición para realizar esta importante labor.

Centraremos nuestra atención, en particular, en las llamadas a funciones. Distinguiremos dos categorías principales de incidencias por las que una función puede no alcanzar sus objetivos: recuperables e irrecuperables. Un lenguaje más reciente, Rust, maneja dichas categorías de forma claramente diferenciada, proporcionando un tipo de retorno Result<T,E> para errores recuperables (el cual contiene un valor de tipo esperado T o bien un elemento de error E) y una macro panic! para detener la ejecución del proceso en caso de producirse una incidencia irrecuperable [1]. Por defecto, C++ trata la primera categoría mediante el uso de excepciones, cuya utilidad pondremos en valor en este post, aunque el empleo de tipos variantes sea sin duda factible gracias a bibliotecas como Boost.Outcome [2]. Como discutiremos también, el tratamiento de errores irrecuperables debería descansar en el uso de contratos.

Invocación inmediata de lambdas (técnica IIFE)

Introducción


En este post describiremos la técnica para realizar inicializaciones locales complejas recomendada por los C++ Core Guidelines. Hablamos aquí de aquellas inicializaciones que involucran típicamente la creación de objetos auxiliares y/o el empleo de sentencias de control. Nos referiremos a dicho procedimiento como IIFE (Immediately-invoked function expression) por su semejanza con la solución idiomática del mismo nombre utilizada en JavaScript.

En el contexto de C++, algunas fuentes prefieren el término IILE (Immediately-invoked lambda expression), dado que la técnica descansa en el empleo de expresiones lambda. Como comprobaremos a continuación, este procedimiento es particularmente útil a la hora de inicializar objetos que deban ser declarados constantes. Para más información, puedes consultar: http://isocpp.github.io/CppCoreGuidelines/CppCoreGuidelines#Res-lambda-init

Range-v3: Una introducción a la biblioteca (Parte III)


En este post proporcionaremos dos nuevos ejemplos de programación con la biblioteca Range-v3, un subconjunto de la cual ha sido adoptada por el nuevo estándar del lenguaje C++20. Pueden consultarse los espacios de nombres utilizados por este artículo en el primer post de la serie.

Range-v3: Una introducción a la biblioteca (Parte II)


En este post proporcionaremos dos nuevos ejemplos de programación con la biblioteca Range-v3, un subconjunto de la cual ha sido adoptada por el nuevo estándar del lenguaje C++20. Pueden consultarse los espacios de nombres utilizados por este artículo en el primer post de la serie.

Range-v3: Una introducción a la biblioteca (Parte I)

Última actualización: 31 de octubre de 2021

Artículos de la serie:


Introducción


La biblioteca header-only Range-v3, desarrollada por Eric Niebler (Facebook), permite la programación con rangos en C++14/17/20 a través de un estilo esencialmente funcional, extensible y de fácil composición [1, 2]. Su excelente acogida en la comunidad de desarrolladores en C++ ha conducido a que parte de la funcionalidad de esta biblioteca haya sido adoptada por el nuevo estándar del lenguaje ISO C++20 [3], a la espera de seguir ampliando su soporte en C++23 [4].