# What Is `parse_context` in fmtlib? The Engine Behind Format String Parsing

> Discover how fmtlib's parse_context powers format string parsing. This state machine manages string traversal, argument indexing, and custom formatter implementation. Learn its vital role.

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

---

**The `parse_context` class serves as the core state machine that fmtlib uses to traverse format strings, managing the remaining character range and argument indexing while providing the essential interface for implementing custom formatters.**

The `parse_context` abstraction is defined in the `fmtlib/fmt` repository within [`include/fmt/core.h`](https://github.com/fmtlib/fmt/blob/main/include/fmt/core.h), where it functions as the mutable environment passed through every stage of format string parsing. This class tracks which portion of the format string remains unprocessed and enforces the library's rules for automatic versus manual argument indexing, making it the backbone of fmtlib's type-safe formatting engine.

## Core Responsibilities: Buffer Management and Index Tracking

`parse_context` maintains two critical pieces of state that enable progressive parsing of format strings.

### The Unprocessed Format String Range

At the heart of `parse_context` lies a `basic_string_view<Char>` member named `fmt_`, which represents the slice of the format string that has not yet been consumed by the parser. As parsing progresses, this view shrinks to exclude specifiers that have already been processed. According to the source code in [`include/fmt/core.h`](https://github.com/fmtlib/fmt/blob/main/include/fmt/core.h) (lines 44-45), this member holds the raw character data that parser methods traverse.

### Argument Index Bookkeeping

The class tracks the next argument identifier through the private member `next_arg_id_` (defined in [`include/fmt/core.h`](https://github.com/fmtlib/fmt/blob/main/include/fmt/core.h), lines 45-46). This integer enables the parser to assign sequential indices to replacement fields like `{}` while also enforcing the mutual exclusivity of automatic and manual indexing (e.g., `{0}`, `{1}`).

## Essential parse_context API Methods

During format string compilation, fmtlib repeatedly invokes methods on a `parse_context` instance to advance through the string and validate argument references.

**Iterator Access**

- **`begin()` and `end()`**: Return iterators over the current format-string slice (lines 61-64 in [`include/fmt/core.h`](https://github.com/fmtlib/fmt/blob/main/include/fmt/core.h)). Custom formatter implementations use these to inspect specifier characters.

**State Progression**

- **`advance_to(it)`**: Shrinks the `fmt_` view by moving the begin iterator to the specified position (lines 66-69). This method is called after a formatter successfully consumes a specifier, ensuring subsequent parsing starts at the correct offset.

**Index Management**

- **`next_arg_id()`**: Returns the next automatically-assigned argument index and increments the internal counter. This method throws if manual indexing has already been established elsewhere in the format string (lines 71-80).
- **`check_arg_id(id)`**: Switches the parser to manual indexing mode and validates that the supplied argument identifier exists. If automatic indexing was previously used, this transition triggers an error (lines 82-90).

**Dynamic Specifications**

- **`check_dynamic_spec(arg_id)`**: Validates that a dynamic width or precision specifier references a valid argument index, ensuring runtime-specified formatting parameters are type-safe (lines 95-97).

## Implementing Custom Formatters with parse_context

Every user-defined formatter receives a reference to a `parse_context` (typically accessed via the alias `fmt::format_parse_context`) through its `parse` method. This contract allows custom types to interpret format specifiers while maintaining consistency with fmtlib's indexing rules.

The following example demonstrates a custom formatter that reverses a string, showing how to interact with the parse context:

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

template <>
struct fmt::formatter<std::string> {
  constexpr auto parse(fmt::format_parse_context& ctx) {
    // Access iterators to the current specifier segment
    auto it = ctx.begin();
    auto end = ctx.end();
    
    // This formatter accepts no custom flags; expect immediate '}'
    if (it != end && *it != '}')
      throw fmt::format_error("invalid format specifier");
    
    // Return iterator to mark parsing completion
    return it;
  }

  template <typename FormatContext>
  auto format(const std::string& s, FormatContext& ctx) const {
    auto out = ctx.out();
    std::copy(s.rbegin(), s.rend(), out);
    return out;
  }
};

// Usage
std::string result = fmt::format("{:}", std::string("hello")); 
// result == "olleh"

```

In this implementation, `ctx.begin()` provides the start of the specifier range, while returning the iterator signals that parsing is complete. The context ensures that if this formatter were used with automatic indexing like `{}`, the library would correctly track the argument position through `next_arg_id()`.

## Handling Argument Indices Explicitly

For formatters that need to manage argument references manually, `parse_context` provides strict validation:

```cpp
struct indexed_formatter {
  int id_ = 0;
  
  constexpr auto parse(fmt::format_parse_context& ctx) -> decltype(ctx.begin()) {
    // Switch to manual indexing and validate the ID
    id_ = 2; // Example: formatter always uses argument 2
    ctx.check_arg_id(id_);
    return ctx.begin();
  }
  
  template <typename FormatContext>
  auto format(int value, FormatContext& ctx) const {
    return fmt::format_to(ctx.out(), "arg[{}] = {}", id_, value);
  }
};

```

Here, `check_arg_id(2)` validates that at least three arguments were provided to the format call and establishes that this replacement field uses manual indexing. If another field in the same format string used automatic indexing (`{}`), this call would raise a compile-time or runtime error, enforcing fmtlib's mixing restriction.

## Integration with fmtlib's Parsing Engine

The `parse_context` abstraction unifies parsing logic across the entire library. Higher-level components rely on this interface:

- **[`include/fmt/core.h`](https://github.com/fmtlib/fmt/blob/main/include/fmt/core.h)**: Defines the `parse_context` template and its public API.
- **[`include/fmt/format.h`](https://github.com/fmtlib/fmt/blob/main/include/fmt/format.h)**: Implements standard formatter specializations using the `parse_context` interface for built-in types like integers and strings.
- **[`include/fmt/ranges.h`](https://github.com/fmtlib/fmt/blob/main/include/fmt/ranges.h)**: Leverages `parse_context` to parse range format specifiers, demonstrating real-world usage with complex nested types.
- **`test/format-test.cc`**: Contains extensive test cases illustrating the contract between custom formatters and the context object.

By centralizing state management in `parse_context`, fmtlib ensures that all formatters—whether built-in or user-defined—operate within a consistent, type-safe environment that prevents indexing errors and buffer overruns.

## Summary

- **`parse_context`** is the mutable state object that tracks the current position in a format string and manages argument indexing during parsing.
- It maintains a **`basic_string_view`** representing the unparsed portion of the format string, accessed via **`begin()`** and **`end()`**.
- The class enforces the mutual exclusivity of automatic (`{}`) and manual (`{0}`) argument indexing through **`next_arg_id()`** and **`check_arg_id()`**.
- Custom formatter implementations receive a **`format_parse_context&`** in their **`parse`** method to inspect specifiers and advance the parse position via **`advance_to()`**.
- All parsing operations in `fmtlib/fmt`—from basic types to ranges—utilize this unified interface defined in **[`include/fmt/core.h`](https://github.com/fmtlib/fmt/blob/main/include/fmt/core.h)**.

## Frequently Asked Questions

### What is the difference between parse_context and format_context?

While `parse_context` manages the format string itself and argument indexing during the parsing phase, `format_context` (typically `fmt::format_context`) handles the output phase by providing the output iterator and access to argument values. The `parse` method of a formatter receives a `parse_context` to read specifiers, whereas the `format` method receives a `format_context` to write data. This separation ensures that parsing logic cannot accidentally modify output state.

### How does parse_context prevent mixing automatic and manual indexing?

The `parse_context` tracks whether `next_arg_id()` or `check_arg_id()` has been called. If `next_arg_id()` is invoked first (automatic indexing), subsequent calls to `check_arg_id()` will throw an error, and vice versa. This state check ensures that format strings cannot contain a mixture of `{}` and `{0}` style references.

### Can I manually advance the parser position in a custom formatter?

Yes. While simple formatters can return `ctx.begin()` if they consume no characters, complex formatters should parse their specifier characters and then call `ctx.advance_to(it)` where `it` points past the consumed portion. This updates the internal string view so that the next parser stage begins at the correct position.

### Where is the parse_context class defined in the fmtlib source?

The `parse_context` class template is defined in **[`include/fmt/core.h`](https://github.com/fmtlib/fmt/blob/main/include/fmt/core.h)** (lines 44-97 in the current main branch), where it appears as `basic_parse_context<Char>`. The common alias `format_parse_context` is a typedef for `basic_parse_context<char>`, and `wformat_parse_context` uses `wchar_t`.