# How fmtlib's `arg_ref` Union Handles Dynamic Width and Precision in C++

> Discover how fmtlib's arg_ref union manages dynamic width and precision by deferring resolution until format time. Learn to use dynamic specifiers effectively.

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

---

**The `arg_ref` union stores either a positional index or a named identifier to defer width and precision resolution until formatting time, enabling dynamic specifiers like `"{:*}"` or `"{:{w}}"`.**

The fmtlib/fmt repository implements high-performance C++ formatting using compact data structures to manage format specifications. When format strings contain dynamic width or precision arguments—specified with `*` for positional indices or `{name}` for named references—the library must store a lightweight reference for later resolution. The `arg_ref` union defined in [`include/fmt/core.h`](https://github.com/fmtlib/fmt/blob/main/include/fmt/core.h) serves as this carrier, distinguishing between integer indices and string views without consuming extra memory.

## The `arg_ref` Union Structure

The `arg_ref` union is a template class defined in [`include/fmt/core.h`](https://github.com/fmtlib/fmt/blob/main/include/fmt/core.h) (lines 74-81) that efficiently packs two distinct reference types into a single memory location.

```cpp
template <typename Char> union arg_ref {
  constexpr arg_ref(int idx = 0)       : index(idx) {}
  constexpr arg_ref(basic_string_view<Char> n) : name(n) {}

  int                 index;   // positional reference
  basic_string_view<Char> name; // named reference
};

```

The union provides **implicit constructors** for both integral indices and string views, allowing the parser to assign values naturally without explicit type specification. Because `index` and `name` share storage, the union remains small—sized to the larger of the two members—while supporting both reference styles required by the C++20 `std::format` specification.

## Storing References in `dynamic_format_specs`

The formatting engine stores `arg_ref` instances within the `dynamic_format_specs` structure, also located in [`include/fmt/core.h`](https://github.com/fmtlib/fmt/blob/main/include/fmt/core.h). This structure maintains separate references for width and precision adjustments:

```cpp
arg_ref<Char> width_ref;      // reference for dynamic width
arg_ref<Char> precision_ref;  // reference for dynamic precision

```

When encountering a `*` character in a format string like `"{:*}"`, the parser immediately instantiates an `arg_ref` and assigns it to `width_ref`. Similarly, precision specifiers like `"{:.{}f}"` trigger storage in `precision_ref`. These references remain unresolved until the actual formatting phase, enabling runtime-dependent layout calculations.

## Parsing Dynamic Specifications

The conversion from format string syntax to `arg_ref` instances occurs in [`include/fmt/format.h`](https://github.com/fmtlib/fmt/blob/main/include/fmt/format.h) through the `parse_dynamic_spec` function and its helper class `dynamic_spec_handler` (lines 71-87).

```cpp
template <typename Char> struct dynamic_spec_handler {
  parse_context<Char>& ctx;
  arg_ref<Char>&      ref;
  arg_id_kind&        kind;

  constexpr void on_index(int id) {
    ref = id;                     // stores positional index
    kind = arg_id_kind::index;
    ctx.check_arg_id(id);
    ctx.check_dynamic_spec(id);
  }
  constexpr void on_name(basic_string_view<Char> id) {
    ref = id;                     // stores named reference
    kind = arg_id_kind::name;
    ctx.check_arg_id(id);
  }
};

```

The `dynamic_spec_handler` receives parsing events and populates the `arg_ref` accordingly. **Positional arguments** trigger `on_index()`, which assigns the integer directly to the union. **Named arguments** trigger `on_name()`, storing the `basic_string_view`. The accompanying `arg_id_kind` enum tracks which union member is active, ensuring type-safe access later.

## Resolving Values at Format Time

When the library renders output, it resolves stored references through `handle_dynamic_spec` in [`include/fmt/format.h`](https://github.com/fmtlib/fmt/blob/main/include/fmt/format.h) (lines 39-51). This function bridges the gap between the stored `arg_ref` and the actual argument values provided by the user.

```cpp
template <typename Context>
constexpr void handle_dynamic_spec(arg_id_kind kind, int& value,
                                   const arg_ref<typename Context::char_type>& ref,
                                   Context& ctx) {
  if (kind == arg_id_kind::none) return;
  auto arg = (kind == arg_id_kind::index) ? ctx.arg(ref.index)
                                           : ctx.arg(ref.name);
  if (!arg) report_error("argument not found");
  ullong result = arg.visit(dynamic_spec_getter());
  if (result > to_unsigned(max_value<int>()))
    report_error("width/precision is out of range");
  value = static_cast<int>(result);
}

```

The implementation checks the `kind` parameter to determine whether to access `ref.index` or `ref.name`. It retrieves the argument from the formatting context, validates that it contains an integral type, verifies the value fits within integer limits, and finally writes the resolved value into the specification struct. This delayed resolution allows the same format string to produce different output widths based on runtime data without re-parsing.

## Practical Usage Examples

Dynamic specifications enable flexible formatting where layout parameters are determined at runtime rather than compile time.

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

int main() {
    // Positional dynamic width: argument 0 provides width 5
    fmt::print("{:*}\n", 5, 12345);          // prints " 12345"

    // Named dynamic precision: argument "p" provides precision 2
    fmt::print("{:{p}.2f}\n", fmt::arg("p", 2), 3.14159);
    // prints "3.14"
}

```

In the first call, the `*` creates a `dynamic_format_specs` instance with `width_ref` holding index `0`. During formatting, `handle_dynamic_spec` fetches argument `0` (the value `5`) and applies it as the minimum field width.

In the second call, the `{p}` syntax causes `parse_dynamic_spec` to store the name `"p"` in `precision_ref`. At resolution time, `handle_dynamic_spec` looks up the named argument, confirms it contains the integer `2`, and sets the floating-point precision accordingly.

## Summary

- The **`arg_ref` union** in [`include/fmt/core.h`](https://github.com/fmtlib/fmt/blob/main/include/fmt/core.h) stores either an `int` index or a `basic_string_view` name to reference formatting arguments.
- **`dynamic_format_specs`** contains two `arg_ref` members (`width_ref` and `precision_ref`) to support runtime-dependent layout.
- The **`dynamic_spec_handler`** class populates the union during parsing, distinguishing between positional and named references using the `arg_id_kind` enum.
- **`handle_dynamic_spec`** resolves these references during formatting by querying the context, validating integer types, and applying range checks.
- This architecture allows fmtlib to support Python-style dynamic width and precision while maintaining zero-overhead abstraction principles.

## Frequently Asked Questions

### What is the purpose of the `arg_ref` union in fmtlib?

The `arg_ref` union provides a type-safe, memory-efficient mechanism to store references to formatting arguments that supply dynamic width or precision values. It consolidates positional indices (integers) and named references (string views) into a single construct, allowing the parser to defer value resolution until the actual formatting phase.

### How does fmtlib differentiate between positional and named dynamic arguments?

The library uses a separate `arg_id_kind` enumeration alongside the `arg_ref` union to track which member is active. During parsing, `dynamic_spec_handler::on_index()` sets the kind to `arg_id_kind::index`, while `on_name()` sets it to `arg_id_kind::name`. The `handle_dynamic_spec` function checks this kind at runtime to determine whether to access `ref.index` or `ref.name`.

### Where does the actual validation of dynamic width and precision values occur?

Validation occurs in `handle_dynamic_spec` within [`include/fmt/format.h`](https://github.com/fmtlib/fmt/blob/main/include/fmt/format.h). This function verifies that the referenced argument exists, extracts its integral value using `dynamic_spec_getter`, confirms the value does not exceed `max_value<int>()`, and reports errors for out-of-range or missing arguments before applying the value to the format specification.

### Can `arg_ref` store types other than integers or string views?

No, the `arg_ref` union is specifically designed to hold only two types: an `int` representing a positional argument index, and a `basic_string_view<Char>` representing a named argument identifier. The actual width or precision values (which may be any integral type) are stored separately in the `dynamic_format_specs` struct and populated only after resolution through `handle_dynamic_spec`.