Las bibliotecas de formato de texto contienen funcionalidades para mostrar
datos en ciertas representaciones de texto.
En el estándar de C++ se incluyen printf
para la biblioteca libc
,
y std::ostream
para libc++
. Fuera del estándar existen otras cuantas
bibliotecas ampliamente usadas, como Boost Format, Fast Format, y fmt.
La sintaxis para mostrar en pantalla el valor del entero x
con un ancho de 4
en algunas de las bibliotecas nombradas es la siguiente:
printf("%4d\n", x); // libc
std::cout << std::setw(4) << x << '\n'; // libc++
std::cout << boost::format("%|4|\n") % x; // Boost Format
ff::fmtln(std::cout, "{0,4}\n", x); // Fast Format
std::cout << folly::format("{:4}\n", x); // Folly Format
fmt::print("{:4}\n", x); // fmt
Python str.format()
Uno de los métodos de formato de cadenas en Python es str.format()
el cual
contiene “campos de reemplazo” rodeados por llaves {campo}
. Todo lo que no
esté contenido entre llaves se considera texto literal, el cual es copiado sin
modificaciones a la salida. 1
Este método fue introducido en la versión 2.6 como una funcionalidad similar
a la del operador módulo (%
) en cadenas, pero mucho más versátil. 2
La biblioteca fmt
{fmt}
es una biblioteca de formato, de código abierto, una alternativa rápida
y segura frente a stdio
(de C) y iostreams
(de C++). 3
Esta biblioteca pretende acercarse a la funcionalidad de Python str.format
,
como una forma expresiva, rápida, segura, e intuitiva de procesar texto con formato.
fmt::format("La respuesta es {}.", 42);
fmt::print(stderr, "Código de error = {}\n", errno);
fmt
se define como: más segura, simple, y expresiva que printf
, pues no usa
punteros, y tiene más validaciones de tipos; más legible que iostreams
, pues
su sintaxis es más intuitiva; sin los inconvenientes que ambos tiene en cuanto
a traducciones; y de desempeño sobresaliente usando variadic templates
(parameter packs), y no varargs (que genera binarios de mayor tamaño,
no tiene acceso aleatorio, y hace más complejo lidiar con argumentos posicionales).
Ejemplos de la sintaxis
-
Argumentos posicionales: Los argumentos pueden ser accedidos de acuerdo a su posición.
fmt::format("{}, {}, {}", 'a', 'b', 'c'); // "a, b, c" fmt::format("{0}, {1}, {2}", 'a', 'b', 'c'); // "a, b, c" fmt::format("{2}, {1}, {0}", 'a', 'b', 'c'); // "c, b, a" fmt::format("{0}{1}{0}", "abra", "cad"); // "abracadabra"
-
Alineamiento del texto: izquierda, derecha, centro, con ancho fijo o dinámico.
fmt::format("{:<30}", "alineado a la izquierda"); // fijo // "alineado a la izquierda " fmt::format("{:<{}}", "alineado a la izquierda", 30); // dinámico // "alineado a la izquierda "
-
Precisión en flotantes: fija o dinámica
fmt::print("{:.1f}", 3.14159265359); // fija // "3.1" fmt::print("{:.{}f}", 3.14159265359, 4); // dinámica // "3.1416"
-
Fechas: chrono
#include <fmt/chrono.h> int main() { auto t = tm(); t.tm_year = 2021 - 1900; t.tm_mon = 1; t.tm_mday = 10; t.tm_hour = 12; t.tm_min = 15; t.tm_sec = 30; fmt::print("{:%Y-%m-%d %H:%M:%S}", t); return 0; }
Resultado:
2021-02-10 12:15:30
Para una descripción más avanzada del uso de fmt/chrono ver la publicación fechas en formato estándar
std::format
La biblioteca estándar de formato de texto está disponible desde C++20
.
std::format
surge a partir de fmt::format
, por lo cual su sintaxis es la misma:
std::string mensaje = std::format("La respuesta es {}.", 42);
Es compatible con iostreams
y tiene un desempeño muy superior al de
Boost Format.
fmt en Conan
La forma más sencilla (para mí) de usar la biblioteca de fmt
en un proyecto
cmake
es por medio de Conan:
Primero, se elige la receta de la versión de fmt a usar:
# conanfile.txt
[requires]
fmt/7.1.2
[generators]
cmake_find_package
Segundo, se enlaza la biblioteca al proyecto:
# CMakeLists.txt
#...
set(CMAKE_MODULE_PATH ${CMAKE_MODULE_PATH} ${CMAKE_BINARY_DIR})
find_package(fmt REQUIRED)
#...
target_link_libraries(${PROJECT_NAME} PRIVATE fmt::fmt)
Tercero, se incluyen los archivos de encabezado que se requieran (core, format, color) en el código fuente:
#include <fmt/core.h>
int main()
{
fmt::print("La respuesta es {}.", 42);
return 0;
}
fmt en spdlog
spdlog
es una biblioteca de logging que usa fmt
de manera interna
(empaquetado), por lo cual hay que tener consideraciones extra si se quieren
usar ambos con Conan.
De todos modos es posible usar spdlog
con {fmt}
de manera externa
por medio de las variables de cmake
:
SPDLOG_FMT_EXTERNAL: "Use external fmt library instead of bundled"
SPDLOG_FMT_EXTERNAL_HO: "Use external fmt header-only library instead of bundled"
fmt en Compiler-explorer
También es posible usar fmt
directamente desde compiler-explorer
seleccionándola de la lista de bibliotecas disponibles.
Benchmarks para fmt
Algunas pruebas de referencia comparando la velocidad muestran que su desempeño
es cercano al de printf
(y en algunos casos superior a este 4), y muy
superior a los demás:
Método | Tiempo de ejecución (segundos) |
---|---|
printf | 1.3 |
fmt::print | 1.46 |
folly format | 2.54 |
std::ostream | 3.35 |
tinyformat | 3.69 |
boost::format | 8.4 |
Benchmark de TinyFormat5.
Fuentes
- Victor Zverovich: std::format in C++20
- {fmt}: A modern formatting library
- CppCon 2017
-
Ver python str.format ↩
-
Ver fmt overview ↩
-
Ver fmt benchmarks ↩
-
Ver fmt performance en CppCon 2017. ↩
Deja un comentario