# How fmtlib's printf API Supports POSIX Positional Arguments

> Explore how fmtlib's printf API supports POSIX positional arguments using %n$ syntax. Learn about out-of-order selection and dynamic formatting in this detailed guide.

- Repository: [Hello World Foundation/fmt](https://github.com/fmtlib/fmt)
- Tags: internals
- Published: 2026-09-05

---

**The fmt library implements POSIX positional arguments in its printf compatibility layer through the `%n$` syntax, enabling out-of-order argument selection and dynamic width/precision specification via the `parse_header` parser in [`include/fmt/printf.h`](https://github.com/fmtlib/fmt/blob/main/include/fmt/printf.h).**

The fmtlib/fmt repository provides a modern C++ formatting library that includes a printf-compatible API for legacy code migration. This implementation fully supports POSIX positional arguments, allowing format strings to reference arguments by index rather than strict sequential order. The feature is implemented through specialized parsing logic that interprets the `$` directive within format specifiers.

## POSIX Positional Argument Syntax

The fmtlib printf API recognizes the POSIX standard `%n$` syntax, where `n` specifies the argument index (1-based) to use for that conversion specification.

### Direct Argument Indexing

To select arguments out of order, use the format `%n$` followed by the conversion specifier. For example, `%2$d` selects the second argument for integer formatting. In [`include/fmt/printf.h`](https://github.com/fmtlib/fmt/blob/main/include/fmt/printf.h), the `parse_header` function (lines 313-342) reads the digit sequence and checks for the `$` character. When found, it treats the value as an argument index (`arg_index`) rather than a width or flag.

The retrieved index is passed to the `get_arg` lambda inside `vprintf` (lines 13-24), which fetches the corresponding argument from the argument list. A negative value passed to `get_arg` indicates "next argument" mode, while non-negative values force the specific index (adjusted for zero-based internal counting).

### Positional Width and Precision

POSIX positional arguments extend to dynamic widths and precisions:

- **Positional width**: `%*2$d` uses the second argument as the field width. When `parse_header` encounters `*` followed by digits and `$` (lines 59-73), it retrieves the specified argument and passes it to `printf_width_handler`.
- **Positional precision**: `%.*3$f` uses the third argument as precision. After parsing the dot, if `*` appears with a digit-`$` sequence (lines 54-71), the parser calls `printf_precision_handler` with the chosen argument.

## Implementation Details in include/fmt/printf.h

The core logic resides in the `parse_header` function, which serves as the entry point for parsing printf-style format specifiers.

### Argument Index Parsing

When processing a format string, `parse_header` first accumulates digits. If the next character is `$`, the accumulated value becomes `arg_index`. This index directs the subsequent `get_arg` call to fetch the correct argument from the `basic_format_args` object. The implementation ensures that once a positional argument is used, the parser maintains index consistency throughout the rest of the format string.

### The get_arg Resolution Mechanism

Inside the `vprintf` function, the `get_arg` lambda (lines 13-24) handles argument retrieval. It receives the parsed index and determines whether to access arguments sequentially or by absolute position. This mechanism supports mixing positional and non-positional specifiers, though POSIX compliance typically requires choosing one style per format string.

### Type Conversion via convert_arg

After argument selection, `convert_arg` (referenced around lines 65-73) adapts the value to match any length modifiers (`h`, `l`, `ll`, etc.). This conversion step operates identically for both positional and non-positional arguments because `get_arg` has already resolved the specific value from the argument pack.

## Practical Code Examples

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

// Simple positional argument - print the second argument first
fmt::printf("%2$s %1$s!\n", "world", "Hello");  // Output: Hello world!

// Positional width - the width is taken from the second argument (10)
int width = 10;
fmt::printf("%*2$s %1$s\n", "right", width);  // Creates:       right

// Positional precision - the precision comes from the fourth argument (3)
int prec = 3;
double value = 1.23456;
fmt::printf("%.*4$f %1$s\n", "unused", value, "unused", prec);  // Output: 1.235

```

In the first example, `%2$s` selects the string "Hello" (argument 2) before `%1$s` selects "world". The second example demonstrates `%*2$s`, where the asterisk requests a width argument and `2$` specifies which argument provides it. The third example shows `%.*4$f`, where the precision is drawn from the fourth argument.

## Summary

- ** fmtlib's printf API** implements POSIX positional arguments through the `%n$` syntax in [`include/fmt/printf.h`](https://github.com/fmtlib/fmt/blob/main/include/fmt/printf.h).
- The **`parse_header`** function detects positional modifiers by scanning for digit sequences followed by `$`, handling both direct argument references and width/precision specifiers.
- **Argument resolution** occurs through the `get_arg` lambda in `vprintf`, which supports negative values for sequential access and non-negative values for indexed access.
- **Type conversion** is handled uniformly by `convert_arg` after argument selection, supporting standard length modifiers regardless of how the argument was indexed.
- The implementation allows mixing positional width (`*n$`) and precision (`.*n$`) specifiers with direct argument indexing.

## Frequently Asked Questions

### Can I mix positional and non-positional arguments in the same format string?

While the fmtlib implementation technically supports mixing styles through the `get_arg` lambda's logic, POSIX compliance requires using either all positional or all non-positional arguments in a single format string. Mixing styles may lead to undefined behavior or compilation errors depending on the specific format checkers enabled.

### Does using positional arguments impact runtime performance?

According to the source code in [`include/fmt/printf.h`](https://github.com/fmtlib/fmt/blob/main/include/fmt/printf.h), positional arguments introduce minimal overhead compared to standard printf formatting. The `parse_header` function performs the index calculation during parsing, and `get_arg` uses simple array indexing to retrieve arguments. The performance characteristics are comparable to standard argument traversal.

### Which fmtlib functions support POSIX positional arguments?

All printf-compatible functions in the fmt library support this syntax, including `fmt::printf`, `fmt::sprintf`, `fmt::fprintf`, and their wide-character variants. The functionality is centralized in the `vprintf` implementation within [`include/fmt/printf.h`](https://github.com/fmtlib/fmt/blob/main/include/fmt/printf.h), ensuring consistent behavior across the entire printf API surface.

### How does fmtlib handle invalid positional indices?

If a format string specifies an argument index that exceeds the provided argument count, the behavior follows the library's standard error handling mechanisms. The `get_arg` function performs bounds checking against the `basic_format_args` object, typically throwing a `format_error` exception when an out-of-range index is requested.