# How basic_format_context Retrieves Dynamic Width and Precision Values in fmtlib

> Discover how fmtlib's basic_format_context retrieves dynamic width and precision values using handle_dynamic_spec. Learn the internal workings of fmtlib's formatting.

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

---

**`fmtlib` stores dynamic width and precision as argument identifiers during parsing, then retrieves the actual integer values from `basic_format_context` at formatting time using `handle_dynamic_spec`.**

The `fmt` library implements dynamic formatting through a deferred resolution mechanism. When a format string contains runtime-dependent width or precision specifiers using the `*` syntax, `basic_format_context` (defined in [`include/fmt/core.h`](https://github.com/fmtlib/fmt/blob/main/include/fmt/core.h)) serves as the execution environment that bridges parsed format specifications with the actual argument values provided at the call site.

## Understanding Dynamic Format Specifiers

Dynamic width and precision allow format strings to accept field dimensions as runtime arguments rather than compile-time constants.

### What Are Dynamic Width and Precision?

In `fmtlib`, dynamic specifiers use the `*` character or named references to indicate that an argument supplies the numeric value. For example, `"{:*>{}}"` uses the next argument for width, while `"{:{width}}"` references a named argument. These are distinct from static specifiers like `"{:10}"` where the value `10` is embedded directly in the format string.

### The Role of basic_format_context

The `basic_format_context` type is a conditional alias that resolves to either the default `context` (when using internal appenders) or `generic_context<OutputIt, Char>` (for user-provided output iterators). According to the source in [`include/fmt/core.h`](https://github.com/fmtlib/fmt/blob/main/include/fmt/core.h) around line 620, this alias determines how the formatting backend accesses arguments:

```cpp
using basic_format_context =
    conditional_t<std::is_same<OutputIt, appender>::value, context,
                  generic_context<OutputIt, Char>>;

```

This context object provides the `arg()` method used to retrieve dynamic values during the formatting phase.

## The Three-Stage Retrieval Process

The mechanism operates through three distinct phases: parsing, detection, and resolution.

### Stage 1: Parsing the Format String

When the parser encounters a dynamic specifier in [`include/fmt/format.h`](https://github.com/fmtlib/fmt/blob/main/include/fmt/format.h), the `parse_width` or `parse_precision` functions store a reference to the argument in `specs.width_ref_` or `specs.precision_ref_`. The parser records whether the reference is by index or name using `arg_id_kind` and invokes `specs.set_dynamic_width()` or `specs.set_dynamic_precision()` to flag these fields as dynamic.

For indexed arguments, the parser stores `arg_id_kind::index`; for named arguments, it stores `arg_id_kind::name`. This information populates the `arg_ref` structure within the `dynamic_format_specs` object.

### Stage 2: Detecting Dynamic Specifications

After parsing completes, the formatting infrastructure checks `specs.dynamic()` (defined in [`include/fmt/format.h`](https://github.com/fmtlib/fmt/blob/main/include/fmt/format.h) near line 4019) to determine if any width or precision values require runtime resolution. This method inspects internal bit-fields (`width_mask` and `precision_mask`) in the `basic_specs` structure to identify which fields contain argument identifiers rather than literal integers.

### Stage 3: Resolving Values with handle_dynamic_spec

The core retrieval logic resides in `handle_dynamic_spec`, implemented in [`include/fmt/format.h`](https://github.com/fmtlib/fmt/blob/main/include/fmt/format.h) around line 4039. This function accepts:

- The `arg_id_kind` (index or name)
- A reference to the target field (`int&` for `specs.width` or `specs.precision`)
- The stored `arg_ref` containing the identifier
- The current `Context` (specialization of `basic_format_context`)

`handle_dynamic_spec` retrieves the argument via `ctx.arg(ref.index)` or `ctx.arg(ref.name)`, then applies `dynamic_spec_getter` to convert the argument to an unsigned long long. If the argument is not an integral type, the library raises an error via `report_error("width/precision is not integer")`. After validation, the function writes the concrete integer back into the specification's width or precision field.

## Implementation Details in the Source Code

Several key components enable this deferred resolution architecture.

### The basic_format_context Alias

As defined in [`include/fmt/core.h`](https://github.com/fmtlib/fmt/blob/main/include/fmt/core.h), `basic_format_context` abstracts the argument access interface. The `generic_context` specialization provides the `arg()` member function that `handle_dynamic_spec` invokes to fetch dynamic values from the argument list.

### Argument References and arg_ref

The `arg_ref` structure (contained within `dynamic_format_specs` in [`include/fmt/format.h`](https://github.com/fmtlib/fmt/blob/main/include/fmt/format.h)) stores either a numeric index or a string name. This lightweight struct is populated during parsing and consumed during formatting to locate the correct argument within the context.

### Type Safety and Validation

The `dynamic_spec_getter` visitor ensures type safety by accepting only integral types. When `handle_dynamic_spec` calls `arg.visit(dynamic_spec_getter())`, the visitor extracts the value as `unsigned long long`. The implementation validates that the value fits within the expected range before assigning it to the specification's integer fields.

## Practical Code Examples

The following examples demonstrate dynamic width and precision resolution through `basic_format_context`:

```cpp
// Dynamic width using positional argument
fmt::print("{:{}}", 42, 5);  // Retrieves width=5 from second argument
// Output: "   42"

// Dynamic precision for floating-point output
fmt::print("{:.{}f}", 3.14159, 2);  // Retrieves precision=2
// Output: "3.14"

// Dynamic width using named arguments (C++20 syntax)
fmt::print("{:{width}}", 7, fmt::arg("width", 8));
// Retrieves width=8 from named argument
// Output: "       7"

```

In each case, the parser stores the argument reference during format string compilation, and `basic_format_context` resolves the value through `handle_dynamic_spec` at output generation time.

## Summary

- **Parsing phase**: `parse_width` and `parse_precision` in [`include/fmt/format.h`](https://github.com/fmtlib/fmt/blob/main/include/fmt/format.h) store argument identifiers in `specs.width_ref_` or `specs.precision_ref_` when encountering `*` syntax.
- **Detection**: The `dynamic()` method checks bit-fields in `basic_specs` to identify which values require runtime lookup.
- **Resolution**: `handle_dynamic_spec` uses `basic_format_context::arg()` to fetch arguments, converts them via `dynamic_spec_getter`, and validates types before assigning to width/precision fields.
- **Type safety**: Non-integer arguments trigger errors through `report_error`.

## Frequently Asked Questions

### How does basic_format_context differ from generic_context?

`basic_format_context` is a type alias that conditionally resolves to either the default `context` type or `generic_context<OutputIt, Char>` depending on the output iterator. In [`include/fmt/core.h`](https://github.com/fmtlib/fmt/blob/main/include/fmt/core.h), this distinction allows the library to optimize for the common case of internal appenders while supporting user-defined output iterators through `generic_context`.

### What happens if a dynamic width argument is not an integer?

The `handle_dynamic_spec` function applies `dynamic_spec_getter` as a visitor to the retrieved argument. If the argument type is not an integral type, the library invokes `report_error("width/precision is not integer")`, preventing invalid width or precision values from corrupting the output format.

### Can dynamic width use named arguments instead of indices?

Yes. When parsing named references like `{:{width}}`, the parser stores `arg_id_kind::name` in the specification and populates the `arg_ref` with the name string. During formatting, `handle_dynamic_spec` calls `ctx.arg(ref.name)` to resolve the value from the named argument map.

### Which header files contain the dynamic width implementation?

The primary implementation spans two headers: [`include/fmt/core.h`](https://github.com/fmtlib/fmt/blob/main/include/fmt/core.h) defines the `basic_format_context` alias and argument access infrastructure, while [`include/fmt/format.h`](https://github.com/fmtlib/fmt/blob/main/include/fmt/format.h) implements the parsing logic (`parse_width`, `parse_precision`), the `handle_dynamic_spec` resolution function, and the `dynamic_format_specs` structure.