Hola Mundo, de nuevo. En una publicación anterior ya mencionaba cómo hacer un ¡Hola Mundo! en C++. Se ha introducido una característica en el estándar C++23 que modifica la forma en la que se escribe ese primer programa que sirve como ejercicio de introducción a un lenguaje de programación, o que se escribe como prueba inicial al actualizar el sistema operativo, al usar otro compilador, una nueva máquina, al probar una biblioteca, una nueva IDE, al probar pipelines en CI/CD, etc.
std::print
Así como sucede con la mayoría de funcionalidades en C++ hay diversas formas de
escribir mensajes en el stdout, tales como printf
, std::puts()
,
y std::cout
.
A partir del estándar C++23
se puede encontrar la funcionalidad std::print()
en las siguientes versiones de la biblioteca estándar:
Biblioteca | Versión |
---|---|
gcc libstdc++ | 14 |
clang libc++ | 18 |
msvc stl | 19.37 |
std::print
es una funcionalidad que viene de la ya conocida biblioteca
{fmt}1, que ya tenía integradas las funcionalidades de
formato std::format
.
Para usar las funcionalidades basta con usar un compilador con versión
compatible, e indicarle que se usa el estándar -std=c++23
.
Sintaxis y Mini-lenguaje
Las características de formato se pueden ver en la tabla de mini-language2
de la biblioteca {fmt}
, y se refiere a las especificaciones de formato
soportadas. Como especificaciones destacadas se encuentran las facilidades para
el formato binario, hexadecimal, de punto-flotante, de chrono, y de rangos.
La sintaxis se da por medio de campos de reemplazo y argumentos posicionales
envueltos en llaves {}
.
campo de reemplazo | '{' [arg_id] [':' (formato)] '}' |
arg_id | identificador del argumento |
formato | formato de la cadena |
ejemplo | std::print("{0:d}", 42); |
Hola de nuevo
Un nuevo “Hola Mundo”, en comparación con los anteriores, podría verse de la siguiente manera:
Otros ejemplos
En una publicación anterior está más detallada la funcionalidad de
{fmt}
, y aunque la implementación de std::print
puede variar dependiendo del
compilador, sus funcionalidades son las mismas. El ejemplo de algunos
especificadores de formato luce así:
#include <print>
std::print("{0:<6} = {1:#06x} {3:f} {2:} {4:#b} {4:d}\n",
"texto", 0xff, 42, 42.0, 0b10);
// Resultado: texto = 0x00ff 42.000000 42 0b10 2
Explicación
- El argumento
0
está alineado a la izquierda con ancho de 6 caracteres, y contiene la cadenatexto
- El argumento
1
es hexadecimal con prefijo (0x
) de ancho 6 rellenado con ceros - El argumento
3
aparece antes que el argumento 2. Contiene la variable tipodouble
con valor 42, con precisión estándar de 6 caracteres, con formato de punto flotante - El argumento
2
aparece después que el argumento 3. Contiene un entero - El argumento
4
aparece dos veces, la primera es en representación binaria con prefijo (0b
), la segunda es en representación decimal
Motivación
Hay diversos motivos por los cuales std::print
llama la atención.
Una de sus características más interesantes es la validación de tipos de datos
en tiempo de compilación, que incluso tiene detección de errores por medio de
análisis estático.
Es fácilmente extensible, es compatible con los contenedores de la STL, con
la biblioteca de rangos, con la biblioteca de fechas std::chrono
, y con tipos
de datos definidos por usuarios.
Es compatible con Unicode, tiene un gran desempeño según sus pruebas de
referencia benchmark3, y funciona bien con multi-hilos.
Tiene una sintaxis mucho más sencilla que la de iostreams
, y cubre con las
falencias de printf
sin afectar el desempeño.
Algunos temas relacionados que permiten entender un poco más
del funcionamiento de std::printf
incluyen plantillas variádicas (paquetes de
parámetros)4, perfect forwarding5, y argumentos posicionales.
Fuentes
- Vitaut blog: print-in-cpp23
- Cppreference: std::print
- Scott Meyers - Effective Modern C++
- Learn microsoft: Ellipses and Variadic Templates
-
Latest {fmt} syntax ↩
-
Format Specification Mini-Language ↩
-
{fmt} benchmarks ↩
-
Effective Modern C++, capítulo 5. R-Value References, Move Semantics And Perfect Forwarding ↩
Deja un comentario