[Example with most used format options] – [Userdefined type with support for std::format]
- the new format library has similar features as str.format() from Python 3
- type safety like iostream
- simplicity and performance like printf.
Example with most used format options
#include <numbers> // pi, e, sqrt2,...
#include <format>
const int intVal = 4711;
const double dblVal1 = std::numbers::pi;
const double dblVal2 = std::numbers::e;
const double dblVal3 = std::numbers::sqrt2;
std::cout << "no format :" << "intVal=" << intVal << " dblVal=" << dblVal1 << "\n";
std::cout << "empty format :" << std::format("intVal={} dblVal={}\n", intVal, dblVal1);
std::cout << "using arg id :" << std::format("intVal={0} dblVal={1} intValAgain={0}\n", intVal, dblVal1);
std::cout << "left aligned < :" << std::format("intVal={:<8} dblVal={:<8.4};\n", intVal, dblVal1);
std::cout << "right aligned > :" << std::format("intVal={:>8} dblVal={:>8.4};\n", intVal, dblVal1);
std::cout << "default aligned :" << std::format("intVal={:8} dblVal={:8.4};\n", intVal, dblVal1);
std::cout << "centered ^ :" << std::format("intVal={:^8} dblVal={:^8.4};\n", intVal, dblVal1);
std::cout << "fill char . :" << std::format("intVal={:.^8} dblVal={:.^8.4};\n", intVal, dblVal1);
std::cout << "fill char * :" << std::format("intVal={:*^8} dblVal={:*^8.4};\n", intVal, dblVal1);
std::cout << "# bin/dez/hex :" << std::format("intVal={0:#018b} intVal={0:#06} intVal={0:#08x}\n", intVal);
std::cout << "precision :" << std::format("dblVal={0:8.4} dblVal={0:.8}\n", dblVal2);
// Specify format (width, precision) via params
// Remark: cannot be combined with arg ids
const auto width = 8;
const auto prec = 6;
std::cout << "format as params :" << std::format("dblVal={:{}.{}}\n", dblVal3, width, prec);
const bool boolVal{ true };
std::cout << "bool :" << std::format("boolVal={0} boolVal={0:s} boolVal={0:d}\n",
boolVal);
// Floating point formats
const double dblVal4 = 1000 * std::numbers::sqrt2;
const std::string dblValAsString = std::to_string(dblVal4);
std::cout << "double no format :" << std::format("dblVal={}\n", dblVal4);
std::cout << "double to_string :" << std::format("dblVal={}\n", dblValAsString);
std::cout << "double e :" << std::format("dblVal={:e}\n", dblVal4);
std::cout << "double E :" << std::format("dblVal={:E}\n", dblVal4);
std::cout << "double f :" << std::format("dblVal={:f}\n", dblVal4);
std::cout << "double g :" << std::format("dblVal={:g}\n", dblVal4);
std::cout << "double 20.10e :" << std::format("dblVal={:20.10e}\n", dblVal4);
std::cout << "double 20.10f :" << std::format("dblVal={:20.10f}\n", dblVal4);
std::cout << "double 20.10g :" << std::format("dblVal={:20.10g}\n", dblVal4);
// Write to string variable
std::string myText;
std::format_to(std::back_inserter(myText), "intVal={:>8} dblVal={:15.10f} \n", intVal, dblVal1);
std::cout << "write to buffer :" << myText << "\n";
Output:
no format :intVal=4711 dblVal=3.14159
empty format :intVal=4711 dblVal=3.141592653589793
using arg id :intVal=4711 dblVal=3.141592653589793 intValAgain=4711
left aligned < :intVal=4711 dblVal=3.142 ;
right aligned > :intVal= 4711 dblVal= 3.142;
default aligned :intVal= 4711 dblVal= 3.142;
centered ^ :intVal= 4711 dblVal= 3.142 ;
fill char . :intVal=..4711.. dblVal=.3.142..;
fill char * :intVal=**4711** dblVal=*3.142**;
# bin/dez/hex :intVal=0b0001001001100111 intVal=004711 intVal=0x001267
precision :dblVal= 2.718 dblVal=2.7182818
format as params :dblVal= 1.41421
bool :boolVal=true boolVal=true boolVal=1
double no format :dblVal=1414.213562373095
double to_string :dblVal=1414.213562
double e :dblVal=1.414214e+03
double E :dblVal=1.414214E+03
double f :dblVal=1414.213562
double g :dblVal=1414.21
double 20.10e :dblVal= 1.4142135624e+03
double 20.10f :dblVal= 1414.2135623731
double 20.10g :dblVal= 1414.213562
write to buffer :intVal= 4711 dblVal= 3.1415926536
For format specifiers see: https://en.cppreference.com/w/cpp/utility/format/formatter#Standard_format_specification
Remark: The characters ‘{‘ and ‘}’ can be written via std::format by using them twice “{{ and }}” => “{ and }”.
Generic performant method for writing to stdout:
#include <cstdio>
template<typename... Args>
void print(const std::string_view fmt_str, Args&&... args)
{
auto fmt_args{std::make_format_args(args...);
std::string outstr{std::vformat(fmt_str, fmt_args)};
std::fputs(outstr.c_Str(), stdout);
}
Remark: A similar method will be added to C++23.
Userdefined type with support for std::format
Assume you have a user defined stuct:
struct MyPoint
{
double x{};
double y{};
};
You want to print it with std::format:
MyPoint p{1.1, 2.2};
print ("My point: {}\n", p); // => My point [1.1, 2.2]
Then you have to add a specialization for a formatter object:
template<>
struct std::formatter<MyPoint>
{
// same code for most user defined types:
template<typename ParseContext>
constexpr auto parse(ParseContext& ctx) {return ctx.begin();}
// code to build specific output:
template<typename FormatContext>
auto format(const Point& p, FormatContext& ctx)
{return format_to(ctx.out(), "[{0:.2}, {1:.2}]", p.x, p.y}
};