fmt: A modern C++ formatting library

repository·main·Indexed 12 days ago

https://github.com/fmtlib/fmt

A high-performance, type-safe C++ formatting library that implements the C++20 std::format and C++23 std::print standards. It provides a fast and safe alternative to C stdio and C++ iostreams, featuring compile-time format string checking, Unicode support, and extensibility for user-defined types. Includes specialized support for std::chrono, ranges, tuples, and various standard library types.

Tokens
20K
Snippets
100
Records
124
Agent score
97%

What's inside fmt

  1. Overview of {fmt} library

    main

    {fmt} is an open-source C++ formatting library that provides a fast and safe alternative to C stdio and C++ iostreams. It implements the C++20 std::format and C++23 std::print standards and uses a syntax similar to Python's format method.

    Key Features:

    • High Performance: Faster than printf, iostreams, to_string, and to_chars.
    • Safety: Fully type-safe with compile-time format string checking (in C++20) and automatic memory management.
    • Extensibility: Supports user-defined types.
    • Portability: Consistent output across platforms with Unicode support.
    • Small Footprint: Small code size and fast compilation times.
    • Header-only option: Can be enabled via the FMT_HEADER_ONLY macro.
  2. Overview of {fmt} features

    main

    The {fmt} library is a modern C++ formatting library designed as a safe, fast, and extensible replacement for the printf family of functions and C++ iostreams.

    Key capabilities include:

    • Safety: Provides compile-time error reporting for invalid format strings (e.g., using a numeric specifier for a string) and prevents buffer overflows via automatic memory management.
    • Extensibility: Supports standard types, containers, dates, and times out-of-the-box. Users can define custom formatting for their own types and enable compile-time checks for them.
    • Performance: Significantly faster than iostreams and sprintf, particularly for numeric formatting, by minimizing dynamic memory allocations.
    • Unicode Support: Provides portable UTF-8 and char string support across Linux, macOS, and Windows.
    • Fast Compilation & Small Footprint: Uses type erasure to minimize template bloat and reduce compilation times. It also maintains a small binary footprint, with options to disable certain components (like floating-point formatting) for resource-constrained environments.
    • Portability: A self-contained codebase with no external dependencies, requiring only a minimal subset of C++11 (compatible with GCC 4.9, Clang 3.6, MSVC 19.10, and later).
  3. Use the Base API for minimal dependencies

    main

    The fmt/base.h header provides the core formatting functionality for char/UTF-8 with C++20 compile-time checks. It is designed for minimal include dependencies to improve compile times. This is the recommended header when using {fmt} as a library (the default) rather than in header-only mode.

    It provides built-in formatter specializations for:

    • int, long long
    • unsigned, unsigned long long
    • float, double, long double
    • bool
    • char
    • const char*, fmt::string_view
    • const void*
  4. Formatting behavior changes in version 6.1.0

    main

    Version 6.1.0 introduced several significant changes to default formatting behavior:

    • Floating Point: float and double now use the shortest decimal representation with correct rounding by default. Additionally, {fmt} no longer converts float arguments to double, ensuring consistency with std::format and preventing precision artifacts (e.g., 0.1f prints as 0.1 instead of 0.10000000149011612).
    • Octal: The formatting of octal zero with a prefix changed from "00" to "0" (e.g., fmt::print("{:#o}", 0); prints 0).
    • Enums: Enums are now mapped to their correct underlying types instead of int, and enum class types are no longer implicitly converted to int.
  5. Configure fill and alignment

    main

    The align field determines where padding is placed when the width exceeds the value's natural size. To use a custom fill character, you must also specify an alignment.

    OptionEffect
    <Left-align; pad on the right. Default for non-numeric types.
    >Right-align; pad on the left. Default for numeric types.
    ^Center the value; extra padding goes on the right if uneven.

    Custom Fill: Use a character immediately before the alignment specifier (e.g., {:*^10}).

    fmt::format("[{:<10}]", "42");   // Result: "[42        ]"
    fmt::format("[{:>10}]", "42");   // Result: "[        42]"
    fmt::format("[{:^10}]", "42");   // Result: "[    42    ]"
    fmt::format("[{:*^10}]", "42");  // Result: "[****42****]"
  6. Use locale-sensitive formatting (L)

    main

    The L flag enables locale-sensitive formatting for numeric types. It uses the locale supplied to the formatting function (or the global locale) to insert digit grouping characters and decimal points.

    auto loc = std::locale("en_US.UTF-8");
    fmt::format(loc, "{:L}", 1234567890);     // Result: "1,234,567,890"
    fmt::format(loc, "{:.2Lf}", 1234567.89);  // Result: "1,234,567.89"
  7. Understand Format String Syntax and Replacement Fields

    main

    The {fmt} library uses format strings containing replacement fields delimited by braces {}. Text outside these braces is copied to the output unchanged.

    Key Rules:

    • Literal Braces: To output a single { or }, you must double them: {{ or }}.
    • Argument Selection (arg_id):
      • Automatic Indexing: If no arg_id is provided (e.g., {}), arguments are consumed in left-to-right order.
      • Positional Reference: Use an integer (e.g., {0}, {1}) to select a specific argument by its position.
      • Named Reference: Use an identifier that matches a name provided via fmt::arg (e.g., {name}).
    • Constraint: You cannot mix automatic indexing and explicit numeric IDs in the same format string; doing so results in a compile-time error or a format_error at runtime.
    • Format Specification: A : following the arg_id introduces a format_spec (or chrono_format_spec) to control how the value is rendered.
    • Nested Fields: You can use a replacement field inside a width or precision specification to provide those values from an integer argument at runtime. Nested fields must use an arg_id and cannot contain their own format_spec.
    fmt::format("hello, {}", "world");
    // Result: "hello, world"
    
    fmt::format("{1}, {0}!", "world", "hello");
    // Result: "hello, world!"
    
    fmt::format("{greeting}, {name}!",
                fmt::arg("greeting", "hi"), fmt::arg("name", "fmt"));
    // Result: "hi, fmt!"
  8. Format chrono duration, time points, and std::tm

    main

    You can format std::chrono duration and time point types, as well as std::tm, using a specific chrono format specification syntax. The syntax follows this pattern:

    [[fill]align][width][.precision]chrono_specs

    Where chrono_specs consists of conversion specifiers (starting with %) and literal characters.

    Key Rules:

    • Literal characters: Any character other than {, }, or % is copied unchanged to the output.
    • Precision: Valid only for std::chrono::duration types with a floating-point representation type.
    • Calendaric components: Specifiers like %d (day of month) are valid for std::tm and time points, but not for durations.
    • Errors: If a specifier (like %a for weekday) is used on a value that does not contain that information, a fmt::format_error is thrown.
    #include <fmt/chrono.h>
    
    auto t = std::tm();
    t.tm_year = 2010 - 1900;
    t.tm_mon = 7;
    t.tm_mday = 4;
    t.tm_hour = 12;
    t.tm_min = 15;
    t.tm_sec = 58;
    fmt::print("{:%Y-%m-%d %H:%M:%S}", t);
    // Prints: 2010-08-04 12:15:58
  9. Format range types with Range Format Specification

    main

    When formatting range types (like std::vector, std::array, etc.), you can use a specific syntax to control how the container and its elements are displayed.

    Syntax: [n][range_type][: range_underlying_spec]

    • n option: Formats the range without the opening and closing brackets (e.g., h, e, l, l, o instead of [h, e, l, l, o]).
    • range_type: Determines the presentation style:
      • none (default): Standard bracketed format.
      • 's': String format. The range is formatted as a single string. Requires the range elements to be character types.
      • '?s': Debug format. The range is formatted as an escaped string. Requires the range elements to be character types.
    • range_underlying_spec: A specification applied to each individual element within the range. This is parsed based on the formatter of the element's type.

    Constraints:

    • The 'n' option and range_underlying_spec are mutually exclusive with 's' and '?s'.
    • By default, ranges of characters or strings are printed escaped and quoted. However, if any range_underlying_spec is provided (even an empty one), the elements are printed according to that specification instead.
    fmt::print("{}", std::vector{10, 20, 30});
    // Output: [10, 20, 30]
    
    fmt::print("{::#x}", std::vector{10, 20, 30});
    // Output: [0xa, 0x14, 0x1e]
    
    fmt::print("{:n}", std::vector{'h', 'e', 'l', 'l', 'o'});
    // Output: 'h', 'e', 'l', 'l', 'o'
    
    fmt::print("{:s}", std::vector{'h', 'e', 'l', 'l', 'o'});
    // Output: "hello"
    
    fmt::print("{:?s}", std::vector{'h', 'e', 'l', 'l', 'o', '\n'});
    // Output: "hello\n"
    
    fmt::print("{:n:f}", std::array{std::numbers::pi, std::numbers::e});
    // Output: 3.141593, 2.718282
  10. Control numeric signs

    main

    The sign field controls how the sign of a numeric value (signed integers and floating-point) is emitted.

    OptionEffect
    +Always emit a sign (+ for nonnegative, - for negative).
    -Emit - only for negative values. (Default).
    Emit a leading space for nonnegative values and - for negative ones.

    Note: The sign of -0.0 is preserved in floating-point output.

    fmt::format("{:+d} {:+d}", 7, -7);  // Result: "+7 -7"
    fmt::format("{: d} {: d}", 7, -7);  // Result: " 7 -7"
  11. Enable compile-time format string checks

    main

    Compile-time checks for format strings are enabled by default on compilers supporting C++20 consteval.

    • To pass a runtime format string (where the string is not a literal), wrap it in fmt::runtime().
    • For older compilers that do not support C++20 consteval, use the FMT_STRING macro defined in fmt/format.h to enable checks.
    // Using a runtime string
    fmt::print(fmt::runtime(some_variable), "{}", arg);
  12. Set field width and runtime width

    main

    The width is the minimum number of characters the field should occupy. It never causes truncation.

    • Static width: {:6}
    • Runtime width: Use nested replacement fields like {:{} } to pass the width as an argument.

    Unicode Awareness: For strings, width is measured in display columns. East Asian wide/fullwidth characters and common emojis count as two columns; others count as one.

    fmt::format("[{:6}]", 42);    // Result: "[    42]"
    fmt::format("[{:6}]", "hi");  // Result: "[hi    ]"
    fmt::format("[{:{}}]", 42, 6); // Result: "[    42]" (width from argument)