The std::fmt library in C++ provides a modern, fast, and type-safe approach to text formatting, drawing inspiration from Python’s str.format syntax. It serves as a more robust and performant alternative to the C-style printf and the C++ iostreams (std::cout).

C++ version introduction

The core components of the C++ standard library’s formatting capabilities were introduced in C++20.

Methods for formatting custom objects

For a user-defined type to be formatted by the std::fmt library, you must provide a specialization of the std::formatter template. The required methods within this specialization are: 

  • parse(): This method is called during the parsing of the format string. It receives a std::format_parse_context and is responsible for parsing any custom format specifiers for your type, such as "{:spec}". It should be constexpr for compile-time format string validation.
  • format(): This method performs the actual formatting of the object. It takes the object to be formatted and a std::format_context. It then uses std::format_to or a similar function to write the formatted output to the context’s output iterator. 

Example

Here is a simplified example of specializing std::formatter for a custom Point class: 

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
#include <format>
#include <string>

struct Point {
  double x, y;
};

// Specialize the std::formatter for the Point class
template <>
struct std::formatter<Point> {
  // Parse any custom format specifiers (none in this simple example)
  constexpr auto parse(std::format_parse_context& ctx) { return ctx.begin(); }

  // Format the Point object
  auto format(const Point& p, std::format_context& ctx) const {
    return std::format_to(ctx.out(), "({}, {})", p.x, p.y);
  }
};

int main() {
  Point p{1.0, 2.0};
  std::string s = std::format("The point is {}", p); // The point is (1, 2)
}

Before std::fmt

Before the introduction of std::fmt in C++20, developers used several non-standard and open-source mechanisms to get printf-like formatting. These libraries were created to address the type-safety issues of C’s printf while offering a more convenient and sometimes faster alternative to C++’s built-in iostreams. 

The primary non-standard libraries and techniques included:

{fmt} library

The {fmt} library is the most notable open-source, non-standard formatting library, as std::fmt was directly adopted from it. 

  • Syntax: Inspired by Python’s str.format, it uses a {} syntax for placeholders, which is a significant improvement in readability over printf’s specifiers like %d, %s, and %x.
  • Type Safety: It uses C++ variadic templates to perform compile-time argument checks, making it type-safe and preventing errors like passing the wrong argument type for a format specifier.
  • Performance: It was designed to be faster than iostreams and competitive with C’s printf family of functions.
  • Extensibility: User-defined types can be made formattable by specializing a fmt::formatter template, a feature that was carried over to std::fmt

Boost.Format

The Boost.Format library was a popular predecessor that provided a type-safe and extensible printf-like facility. 

  • Syntax: It uses a syntax of %N% to denote the position of arguments, which are passed to the format object using the operator%.
  • Mechanism: Internally, it used C++ iostreams to do the actual formatting. A boost::format object first parses the format string, and then each argument is streamed into it.
  • Extensibility: Any user-defined type that had a std::ostream& operator<< overload could be used with Boost.Format. 

tinyformat.h

This is a minimalist, header-only library that provides a type-safe printf replacement. Git

  • Simplicity: With the code contained in a single header file, it was easy to integrate into projects.
  • Compatibility: It aimed for compatibility with C99 printf() syntax while leveraging C++ features for type safety.
  • Mechanism: It augmented standard stream formatting rather than completely replacing it. 

Variadic templates

Some developers created custom printf-like functions using C++11 variadic templates. 

  • Purpose: These implementations were written to provide a type-safe formatting mechanism for specific use cases or projects, without relying on a third-party library.
  • Mechanism: They typically used a recursive template approach to process each argument in the format string. 

Standard iostreams

While not a “non-standard” solution, many developers chose to use std::cout, std::ostringstream, and iostream manipulators from <iomanip> as an alternative to printf.

  • Extensibility: The main advantage was the ability to define operator<< overloads for custom types.
  • Performance: Iostreams are generally slower than printf and the {fmt} library, a factor that prompted the development of faster alternatives.
  • Complexity: Complex formatting using manipulators was sometimes considered less intuitive than printf or {fmt} syntax.