Skip to content

std::format

[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}
};