# How to Use fmtlib for Printf-Compatible Formatting in C++

> Learn to use fmtlib for printf compatible formatting in C++ with fmt::printf, fmt::sprintf, and fmt::fprintf. Enjoy type-safe, overflow-free string formatting.

- Repository: [Hello World Foundation/fmt](https://github.com/fmtlib/fmt)
- Tags: how-to-guide
- Published: 2026-09-10

---

**The fmt library provides a type-safe, printf-compatible API through [`include/fmt/printf.h`](https://github.com/fmtlib/fmt/blob/main/include/fmt/printf.h), allowing you to use traditional `%` format specifiers with functions like `fmt::printf`, `fmt::sprintf`, and `fmt::fprintf` while eliminating buffer overflows and type mismatches.**

The fmt library (fmtlib/fmt) bridges modern C++ type safety with legacy C formatting syntax. The printf-compatible implementation resides in [`include/fmt/printf.h`](https://github.com/fmtlib/fmt/blob/main/include/fmt/printf.h) and wraps the library's high-performance formatting engine. This allows migration of existing printf codebases without sacrificing compile-time validation or runtime performance.

## Core Architecture of the Printf Implementation

The printf-compatible layer reuses the library's generic formatting infrastructure while parsing classic format strings. The implementation centers on three subsystems: context management, type conversion, and format string parsing.

### Context and Argument Storage

The `basic_printf_context` class in [`include/fmt/printf.h`](https://github.com/fmtlib/fmt/blob/main/include/fmt/printf.h) manages the formatting session. It holds the output destination via `basic_appender` and maintains arguments through `basic_format_args`. This context exposes three critical methods used by the formatting engine: `out()` for buffer access, `arg(int)` for indexed argument retrieval, and `locale()` for localization.

Arguments are stored efficiently using `make_printf_args`, which invokes `fmt::make_format_args<basic_printf_context<Char>>`. This mechanism stores arguments by reference (avoiding copies) and ensures they remain valid for the duration of the formatting call.

### Type Conversion and Length Modifiers

Before output, arguments undergo type conversion via the `convert_arg` template and helper classes `arg_converter` and `char_converter`. These components map the generic `basic_format_arg` to concrete types expected by printf specifiers (`%d`, `%lld`, `%x`, etc.), handling signed/unsigned nuances and standard length modifiers (`h`, `l`, `j`, `z`, `t`).

### Format String Parsing Pipeline

The parsing logic processes format strings through specialized functions that populate a `format_specs` structure:

- `parse_header` extracts positional indexes (`%2$d`), flags, width, and precision
- `parse_flags` interprets alignment indicators and sign options  
- `parse_printf_presentation_type` translates conversion characters (`d`, `x`, `f`, `s`) into internal `presentation_type` values understood by the underlying `detail::vprintf` engine

## Using fmtlib Printf Functions

The public API provides drop-in replacements for C standard library functions with identical syntax but enhanced safety.

### Writing to stdout and Strings

Use `fmt::printf` for direct stdout output and `fmt::sprintf` to return a `std::string`:

```cpp
#include <fmt/printf.h>
#include <string>

// Write to stdout
fmt::printf("Hello %s, the answer is %d\n", "world", 42);

// Return formatted string
std::string s = fmt::sprintf("%08x → %0.2f", 0xDEAD, 3.14159);
// Result: "0000dead → 3.14"

```

### File Output with fmt::fprintf

Write to arbitrary `FILE*` streams using `fmt::fprintf`:

```cpp
#include <fmt/printf.h>
#include <cstdio>

FILE* out = fopen("output.txt", "w");
fmt::fprintf(out, "Hex: %#x, Char: %c\n", 255, 'A');
fclose(out);

```

### Positional Arguments and Dynamic Width

The implementation supports POSIX-style positional arguments and dynamic width/precision using the `*` specifier:

```cpp
// Positional arguments: %2$ uses second arg, %1$ uses first
fmt::printf("%2$*1$d %1$*2$d\n", 5, 3, 7);  // prints "    7     5"

// Dynamic width (10) and precision (4) from arguments
int w = 10, p = 4;
fmt::printf("Value: %*.*f\n", w, p, 1.234567);  // "Value:      1.2346"

```

## Type Safety and Formatting Engine

Unlike traditional `printf`, fmtlib validates types at compile time. The `printf_arg_formatter` class (derived from `arg_formatter`) handles actual output generation, applying parsed specifications including alignment and fill characters. For strings, pointers, and characters, it additionally handles `null` pointer output safely.

Because the implementation delegates to `detail::vprintf`, the same precision, width, and alignment rules apply as with the modern `{}` syntax, ensuring consistent behavior across both formatting styles.

## Summary

- Include `<fmt/printf.h>` to access printf-compatible formatting functions
- `fmt::printf` writes to stdout, `fmt::sprintf` returns `std::string`, and `fmt::fprintf` writes to `FILE*` streams  
- All standard conversion specifiers, length modifiers (`h`, `l`, `ll`, `j`, `z`, `t`), and flags are supported
- Use `%N$` syntax for POSIX positional arguments and `*` for dynamic width/precision
- Arguments are stored by reference via `make_printf_args` for zero-copy efficiency
- The underlying `detail::vprintf` engine ensures consistent performance with modern `{}` formatting

## Frequently Asked Questions

### What is the difference between fmt::printf and fmt::format?

`fmt::printf` uses traditional `%` format specifiers compatible with C's `printf`, while `fmt::format` uses Python-style `{}` placeholders. Both functions utilize the same high-performance formatting engine in [`include/fmt/format.h`](https://github.com/fmtlib/fmt/blob/main/include/fmt/format.h), but `fmt::printf` provides a migration path for legacy codebases requiring `%` syntax compatibility.

### Does fmtlib printf support all standard C printf length modifiers?

Yes. According to the source in [`include/fmt/printf.h`](https://github.com/fmtlib/fmt/blob/main/include/fmt/printf.h), the `arg_converter` class handles standard length modifiers including `h` (short), `l` (long), `ll` (long long), `j` (intmax_t), `z` (size_t), and `t` (ptrdiff_t), properly mapping them to the corresponding C++ types during the conversion phase.

### Is fmt::sprintf safer than std::sprintf?

Yes. `fmt::sprintf` eliminates buffer overflow risks by automatically managing `std::string` memory and prevents type mismatches through compile-time validation. The `printf_arg_formatter` class also safely handles null pointers for string arguments, whereas `std::sprintf` would invoke undefined behavior.

### Can I mix positional and non-positional arguments in fmt::printf?

No, the implementation requires consistency per POSIX specification. You must use either all positional arguments (e.g., `%1$d %2$s`) or all non-positional arguments (e.g., `%d %s`). Mixing styles within a single format string results in a format error.