Reflexión en C++26 (Parte I)

Información para el lector
Tiempo estimado de lectura: 30 minutos
Nivel: Avanzado (C++26, metaprogramación, reflexión estática)

Esta nueva serie de posts pretende introducir al lector en las nuevas capacidades de reflexión estática que formarán parte del próximo estándar ISO C++26, tal y como fueron introducidas en la propuesta P2996 [1] y que ya han sido incorporadas (con algunas modificaciones y extensiones) al draft del estándar [2]. Por reflexión estática entendemos aquí la capacidad del programa de analizar su propia estructura en tiempo de compilación y producir código derivado de ella.

Esta nueva meta-programación reflexiva se basa en:

  1. La representación de elementos del programa mediante expresiones constantes (constant‑expressions) que producen valores de reflexión —reflexiones— del tipo opaco std::meta::info.
  2. El operador unario de reflexión (prefijo ^^), capaz de proyectar múltiples construcciones gramaticales del lenguaje —como namespace-names, type-ids, id-expressions o el espacio de nombres global ::— a sus correspondientes reflexiones.
  3. Un amplio conjunto de operaciones consteval en la cabecera <meta> para trabajar con dichas reflexiones (incluyendo la derivación de otras reflexiones).
  4. Los denominados splicers [:…:] (más formalmente, splice-expressions), que permiten generar construcciones sintácticas a partir de reflexiones.

Biblioteca chrono - Ejemplo de uso: contando los días laborables en un período

En este post analizaremos el empleo de la biblioteca estándar <chrono> [1] para la codificación de un algoritmo sencillo que permita determinar el número de días laborables en un período temporal dado. En nuestros ejemplos haremos uso, entre otras, de la función std::chrono::parse [2] incluida en C++20 y que cuenta con una implementación experimental en el compilador GCC 14.

A modo de caso concreto de estudio, nos centraremos en el ámbito educativo a fin de computar los días lectivos dentro de cualquier intervalo cerrado [sd_0, sd_1], siendo sd_0 y sd_1 dos fechas enmarcadas en un curso académico que cumplan sd_0 ≤ sd_1. Como días lectivos asumiremos todos aquéllos de la semana exceptuando sábados, domingos y festivos.

tl-ranges - Instalación bajo mingw-w64

La biblioteca tl-ranges de Sy Brand [1], distribuida bajo una licencia de dominio público, proporciona una excelente implementación de adaptadores de rangos que no llegaron a ser incluidos en C++20. Entre ellos destacaríamos (los elementos estandarizados en C++23 se indican con un asterisco):

C++23 - Generador síncrono std::generator

El futuro estándar del lenguaje C++23 incorporará la plantilla de clase std::generator (cabecera <generator>) como modo de obtener generadores síncronos basados en corrutinas que modelen el concepto std::ranges::input_range (recordemos que los algoritmos que operan con este tipo de rangos no deben intentar atravesar un mismo iterador input_range más de una vez). Dichos generadores constituyen vistas que sólo pueden moverse (move-only views), con iteradores de igual naturaleza [1].

Como primer ejemplo, consideremos el siguiente generador de números enteros pertenecientes a la sucesión de Fibonacci, es decir, aquélla que comienza con los números 0 y 1 y para la que cada valor subsiguiente se calcula como suma de los dos elementos que lo preceden. Por sencillez, ignoraremos el inevitable desbordamiento de enteros para valores elevados de la sucesión [1]:

   auto fibonacci_gen() -> std::generator<int>    {       auto a = 0,            b = 1;       while (true) {          co_yield std::exchange(a, std::exchange(b, a + b));       }    }

"Hello, world!" en C++23

C++ ha conocido dos estándares en su etapa moderna que, por su alcance y extensión, han revolucionado la manera de programar en el lenguaje. Nos referimos, por supuesto, a C++11 y C++20. El próximo estándar del lenguaje, C++23, puede considerarse una actualización relativamente menor de C++20, de forma análoga a como C++14 y C++17 constituyeron extensiones naturales de C++11. Aun cuando la pandemia por COVID-19 de los últimos años haya afectado al proceso de trabajo habitual del comité ISO WG21, C++23 logrará publicarse en plazo presentando numerosas novedades, entre las que cabe señalar:

  • A nivel del núcleo del lenguaje: if consteval [1], deducción de this [2], atributos en expresiones lambda [3], atributo [[assume]] [4], auto(x) y auto{x} [5], directiva #warning [6].
  • A nivel de la biblioteca estándar: nuevas vistas std::views [7], <expected> [8], operaciones monádicas para std::optional [9], vistas para arrays multidimensionales <mdspan> [10], adaptadores de contenedores asociativos <flat_map> y <flat_set> [11, 12], generadores síncronos std::generator como soporte para corrutinas [13], funciones de impresión std::print y std::println en <print> [14].

Mónadas para programadores en C++: std::optional (Parte I)

Conceptos matemáticos relevantes: Categorías, functores y mónadas

Desde un punto de vista matemático, una categoría C está constituida por [1]:
  1. Una colección Ob(C) de objetos.
  2. Una colección Hom(C) de flechas o morfismos que enlazan dichos objetos. Un morfismo f del objeto X al objeto Y se denota f: X → Y. El conjunto de morfismos entre los objetos X e Y se denota HomC(X, Y).
  3. Una operación binaria de composición de morfismos. La composición de f: X → Y con g: Y → Z se denota g ∘ f: X → Z
Asumimos los siguientes axiomas en relación a la composición:
  • Asociatividad: Si f: A → Bg: B → C h: C → D, entonces h ∘ (g ∘ f) = (h ∘ g) ∘ f.
  • Para cada objeto X, existe un morfismo identidad idXX → X que se comporta como elemento neutro bajo composición, i.e., para todo morfismo f: X → Y se cumple f ∘ idX idY  f = f. Se puede demostrar que idX es único para cada X.