# How `parse_dynamic_spec` Handles Literal and Argument References for Width/Precision in fmtlib

> Learn how fmtlib's parse_dynamic_spec differentiates literal and argument references for runtime width and precision control. Optimize your formatting.

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

---

**`parse_dynamic_spec` distinguishes between literal integer values and dynamic argument references (implicit, explicit positional, or named) to configure width and precision at runtime in the fmt library.**

The fmt library (fmtlib/fmt) provides a high-performance formatting system for C++. When parsing format specifiers like `{:{}}` or `{:10}`, the library relies on `parse_dynamic_spec` to determine whether width or precision values are supplied as compile-time literals or extracted from runtime arguments. This function, defined in [`include/fmt/core.h`](https://github.com/fmtlib/fmt/blob/main/include/fmt/core.h), serves as the critical bridge between static format strings and dynamic formatting parameters.

## How `parse_dynamic_spec` Parses Literal Integers

When `parse_dynamic_spec` encounters a digit, it treats the value as a literal width or precision. The function checks if the first character falls within the `0` to `9` range and delegates to `parse_nonnegative_int` to extract the integer value.

If the input character is a digit, the parser reads the integer directly:

```cpp
if ('0' <= *begin && *begin <= '9') {
    int val = parse_nonnegative_int(begin, end, -1);
    if (val == -1) report_error("number is too big");
    value = val;                     // literal width/precision
}

```

In this branch, the function stores the parsed integer in the `value` reference parameter and leaves the returned `kind` as `arg_id_kind::none` (indicating no argument lookup is required). This logic appears in [`include/fmt/core.h`](https://github.com/fmtlib/fmt/blob/main/include/fmt/core.h) at approximately lines 1398-1406.

## Resolving Dynamic Argument References

When the specifier begins with a `{`, `parse_dynamic_spec` enters dynamic resolution mode, supporting three distinct reference types.

### Implicit Positional Arguments (`{}` or `{:}`)

If the character following the opening brace is `}` or `:`, the parser recognizes an implicit reference to the next argument in the format string. The implementation calls `ctx.next_arg_id()` to retrieve the current positional index, records it in the `ref` parameter, and sets `kind` to `arg_id_kind::index`.

```cpp
if (c == '}' || c == ':') {
    int id = ctx.next_arg_id();
    ref = id;
    kind = arg_id_kind::index;
    ctx.check_dynamic_spec(id);
}

```

This mechanism allows formats like `fmt::format("{:{}}", value, width)` where the second argument supplies the width dynamically. The `check_dynamic_spec` call validates that the referenced argument exists and is suitable for width or precision specification (source: [`include/fmt/core.h`](https://github.com/fmtlib/fmt/blob/main/include/fmt/core.h), lines 1410-1416).

### Explicit Positional and Named References (`{N}` or `{name}`)

For explicit references (e.g., `{2}` or `{width}`), the parser delegates to `parse_arg_id` with a `dynamic_spec_handler` callback. This handler distinguishes between numeric indices and named arguments:

- **Positional index**: Sets `ref = id` and `kind = arg_id_kind::index`
- **Named argument**: Sets `ref = id` and `kind = arg_id_kind::name`

Both cases invoke `ctx.check_dynamic_spec(id)` to validate the reference:

```cpp
begin = parse_arg_id(begin, end,
                     dynamic_spec_handler<Char>{ctx, ref, kind});

```

This branch handles advanced syntax like `{: {2}.{1}}` where specific argument positions define width and precision, or named references such as `{:{width}.{prec}}` (source: [`include/fmt/core.h`](https://github.com/fmtlib/fmt/blob/main/include/fmt/core.h), lines 1417-1419).

## Error Handling for Invalid Specifiers

When `parse_dynamic_spec` encounters malformed braces or unsupported tokens after `{`, it immediately reports an error:

```cpp
report_error("invalid format string");

```

This safety check appears at lines 1422-1425 in [`include/fmt/core.h`](https://github.com/fmtlib/fmt/blob/main/include/fmt/core.h), ensuring that incomplete dynamic specifiers like `{:` or `{invalid` trigger clear compile-time or runtime errors rather than silent failures.

## Integration with Width and Precision Parsing

The `parse_dynamic_spec` function does not operate in isolation. Higher-level functions `parse_width` and `parse_precision` (also in [`include/fmt/core.h`](https://github.com/fmtlib/fmt/blob/main/include/fmt/core.h)) invoke it to populate `format_specs` objects:

```cpp
specs.set_dynamic_width(result.kind);
// or
specs.set_dynamic_precision(result.kind);

```

Later, during the actual formatting phase, `detail::handle_dynamic_spec` (defined in [`include/fmt/format.h`](https://github.com/fmtlib/fmt/blob/main/include/fmt/format.h) around lines 4040-4052) retrieves the argument value using the recorded `kind` and `ref` (index or name) to substitute the final width or precision at runtime.

## Practical Code Examples

Here are concrete examples demonstrating how `parse_dynamic_spec` interprets different specifier types:

```cpp
// 1️⃣ Literal width
fmt::format("{:10}", 42);          // width = 10 (literal)

// 2️⃣ Implicit argument reference for width
fmt::format("{:{}}", 42, 10);      // second argument supplies width (implicit)

// 3️⃣ Explicit positional argument reference for precision
fmt::format("{:{2}.{1}}", 42, 3, 5);
// → width = argument #2 (value 5), precision = argument #1 (value 3)

// 4️⃣ Named argument reference (C++20 named arguments)
fmt::format("{:{width}.{prec}}", fmt::arg("width", 8), fmt::arg("prec", 2), 42);
// → width = named argument "width", precision = named argument "prec"

```

## Summary

- **`parse_dynamic_spec`** in [`include/fmt/core.h`](https://github.com/fmtlib/fmt/blob/main/include/fmt/core.h) serves as the core parser for dynamic width and precision specifiers in the fmt library.
- **Literal integers** (digits `0-9`) are parsed directly via `parse_nonnegative_int` with `kind` set to `arg_id_kind::none`.
- **Implicit references** (`{` followed by `}` or `:`) use `ctx.next_arg_id()` to bind to the next positional argument.
- **Explicit references** delegate to `parse_arg_id` and `dynamic_spec_handler` to support positional indices (`{N}`) or named arguments (`{name}`).
- **Error handling** triggers `"invalid format string"` for malformed dynamic specifiers.
- The function integrates with `parse_width`, `parse_precision`, and later `handle_dynamic_spec` in [`include/fmt/format.h`](https://github.com/fmtlib/fmt/blob/main/include/fmt/format.h) to resolve runtime values.

## Frequently Asked Questions

### How does `parse_dynamic_spec` distinguish between a literal number and an argument reference?

`parse_dynamic_spec` checks the first character of the specifier. If it is a digit (`0` through `9`), the function treats it as a literal and calls `parse_nonnegative_int`. If the character is an opening brace `{`, it initiates argument reference parsing to resolve the value dynamically from the argument list.

### What is the difference between `arg_id_kind::none` and `arg_id_kind::index` in the context of `parse_dynamic_spec`?

`arg_id_kind::none` indicates that the width or precision was specified as a literal integer baked into the format string, requiring no runtime argument lookup. `arg_id_kind::index` (or `arg_id_kind::name`) indicates that the value must be fetched from a specific argument position or named argument during the formatting phase via `handle_dynamic_spec`.

### Can `parse_dynamic_spec` handle named arguments for width and precision?

Yes. When the parser encounters a name inside braces (e.g., `{width}`), `parse_arg_id` with `dynamic_spec_handler` records the identifier and sets `kind` to `arg_id_kind::name`. The actual value resolution occurs later in `handle_dynamic_spec`, which looks up the named argument in the format context.

### Where does the actual argument value get resolved after `parse_dynamic_spec` records the reference?

While `parse_dynamic_spec` (in [`include/fmt/core.h`](https://github.com/fmtlib/fmt/blob/main/include/fmt/core.h)) only records the reference type and identifier, the actual value resolution happens in `detail::handle_dynamic_spec` located in [`include/fmt/format.h`](https://github.com/fmtlib/fmt/blob/main/include/fmt/format.h) (around lines 4040-4052). This function uses the stored `kind` and `ref` to extract the integer value from the argument list at formatting time.