std::format e la formattazione moderna del testo
La produzione di testo formattato è un'esigenza costante in ogni programma: messaggi di log, report, output per l'utente, stringhe da comporre a partire da valori diversi. Per lungo tempo il C++ ha offerto due approcci imperfetti a questo scopo: gli stream, potenti ma verbosi, e le funzioni di formattazione ereditate dal C, concise ma prive di sicurezza sui tipi. Lo standard C++20 introduce std::format, una soluzione moderna che unisce concisione, sicurezza e leggibilità.
I limiti degli approcci precedenti
Comporre una stringa con gli stream richiede di concatenare più operatori di inserimento, e la gestione della formattazione numerica, come il numero di cifre decimali, obbliga a manipolare manipolatori poco intuitivi. Le funzioni in stile C, dal canto loro, usano stringhe di formato con segnaposto, ma non verificano la corrispondenza tra i segnaposto e i tipi effettivi degli argomenti, aprendo la porta a errori gravi e difficili da individuare. La funzione std::format risolve entrambi i problemi.
La sintassi di base
La funzione std::format, definita nell'intestazione format, riceve una stringa di formato contenente segnaposto indicati da parentesi graffe, seguita dagli argomenti che li sostituiscono nell'ordine. Il risultato è una nuova stringa, che può essere stampata, memorizzata o ulteriormente elaborata.
#include <format>
#include <string>
#include <iostream>
int main() {
std::string name = "Mario";
int age = 30;
// I segnaposto vengono sostituiti in ordine
std::string message = std::format("{} ha {} anni", name, age);
std::cout << message << std::endl;
return 0;
}
A differenza delle funzioni in stile C, non occorre indicare il tipo di ciascun argomento nel segnaposto: std::format lo deduce automaticamente e verifica in fase di compilazione che la stringa di formato sia coerente con gli argomenti forniti, prevenendo un'intera categoria di errori.
Argomenti posizionali
All'interno delle parentesi graffe è possibile indicare un indice numerico per riferirsi a un argomento specifico, indipendentemente dall'ordine. Questo consente di riutilizzare lo stesso argomento più volte o di cambiarne l'ordine di apparizione nella stringa senza modificare la lista degli argomenti, funzionalità preziosa soprattutto nella localizzazione dei testi.
// L'indice permette di riordinare e ripetere gli argomenti
std::string text = std::format("{0} e {1}, ma anche {0}", "primo", "secondo");
Specificatori di formato
Dopo un carattere di due punti all'interno delle parentesi graffe è possibile indicare specificatori che controllano l'aspetto del valore: l'allineamento, la larghezza minima del campo, il numero di cifre decimali, la base numerica e altro ancora. Questa mini-grammatica offre un controllo dettagliato sulla presentazione, rimanendo compatta e leggibile.
#include <format>
double price = 3.14159;
// Due cifre decimali
std::string rounded = std::format("{:.2f}", price); // "3.14"
// Campo largo dieci, allineato a destra
std::string aligned = std::format("{:>10}", 42);
// Rappresentazione esadecimale
std::string hex = std::format("{:x}", 255); // "ff"
Stampare direttamente con std::print
Lo standard C++23 aggiunge std::print e std::println, nell'intestazione print, che combinano formattazione e stampa in un'unica operazione, evitando di dover comporre prima la stringa e stamparla poi. La variante con il suffisso aggiunge automaticamente un ritorno a capo. Questo rende l'output formattato ancora più diretto e conciso.
#include <print>
int main() {
// Formatta e stampa in un solo passaggio
std::println("Il totale e {} euro", 120);
return 0;
}
Formattare tipi personalizzati
Il sistema di formattazione è estensibile: è possibile insegnare a std::format come rappresentare i propri tipi, specializzando un apposito modello di formattazione. Una volta definita la regola, gli oggetti del tipo personalizzato possono comparire nei segnaposto esattamente come i tipi predefiniti, con la stessa sicurezza e le stesse possibilità di controllo. Questa estensibilità permette di integrare in modo uniforme le classi del proprio dominio nel sistema di formattazione standard.
Un salto di qualità
La formattazione moderna rappresenta un netto miglioramento rispetto al passato. Unisce la concisione delle funzioni in stile C alla sicurezza sui tipi degli stream, aggiungendo un controllo in fase di compilazione che elimina molti errori. Con std::format e, in C++23, con std::print, comporre e produrre testo formattato diventa un'operazione semplice, sicura ed espressiva, chiudendo una lacuna che il linguaggio si portava dietro da decenni.