# How the Compile API in fmtlib Performs Compile-Time Format String Parsing

> Explore how fmtlib's compile API uses compile-time parsing via a constexpr state machine to build type-safe formatting objects, eliminating runtime overhead for faster applications.

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

---

**The `fmt::compile` API eliminates runtime format string overhead by parsing literals at compile time through a constexpr recursive state machine that builds type-safe formatting objects.**

The `{fmt}` library (fmtlib/fmt) provides a **compile-time formatting API** implemented in [`include/fmt/compile.h`](https://github.com/fmtlib/fmt/blob/main/include/fmt/compile.h) that transforms format strings into optimized formatting structures during compilation. Unlike standard runtime formatting, this approach resolves argument types, specifiers, and field positions entirely at compile time, generating machine code that writes output directly without parsing strings at runtime.

## The Compile-Time Parsing Workflow

The compile API follows a strict pipeline that converts string literals into executable formatting objects through template metaprogramming and constexpr evaluation.

### Marking Literals for Compilation

The process begins with the `FMT_COMPILE(s)` macro defined in [`include/fmt/compile.h`](https://github.com/fmtlib/fmt/blob/main/include/fmt/compile.h) (lines 37‑40). This macro wraps a string literal and converts it into a `compiled_string` type when the compiler supports C++17 `if constexpr` and return-type deduction. If these features are unavailable, it falls back to standard `FMT_STRING` behavior.

When you invoke `fmt::format(FMT_COMPILE("value: {}"), 42)`, the compiler selects an overload of `fmt::format` that accepts `compiled_string` (lines 54‑59). This overload forwards the literal to `detail::compile<T...>(S{})`, where `S` represents the compiled literal type and `T...` captures the argument type list.

### The constexpr Recursive Parser

Inside [`include/fmt/compile.h`](https://github.com/fmtlib/fmt/blob/main/include/fmt/compile.h) (lines 61‑69), `detail::compile<T...>(S{})` creates a `basic_string_view` of the literal and invokes `detail::compile_format_string<type_list<T...>, 0, 0, ...>(fmt)`. This function operates as a **constexpr recursive state machine** that walks the format string character by character at compile time.

The parser distinguishes three cases for each character (lines 82‑95):
- **Opening brace `{`**: Initiates replacement field parsing
- **Closing brace `}`**: Handles escaped braces or syntax errors  
- **Ordinary text**: Accumulates literal characters via `parse_text`

For ordinary text sequences, `parse_text` locates the next brace and creates either a `text` node (multiple characters) or a `code_unit` node (single character), returning the remainder of the string for further processing.

### Parsing Replacement Fields

When encountering an opening brace, the parser determines the field type through `parse_replacement_field_then_tail` (lines 100‑138). It handles:
- **Escaped braces**: `{{` and `}}` become static text nodes
- **Positional fields**: `{}` or `{:}` use automatic argument indexing
- **Indexed fields**: `{N}` specifies the Nth argument explicitly
- **Named fields**: `{name}` resolves to named arguments (C++20)

The helper `parse_arg_id` extracts argument identifiers, while format specifiers following the colon (e.g., `{:04x}`) trigger `parse_specs` to create a `spec_field` rather than a plain `field`.

### Type Resolution and Tree Construction

The template `get_type<N, Args>` (lines 15‑22) retrieves the compile-time type of the Nth argument from the accumulated `type_list<Args...>`. This enables the parser to instantiate strongly-typed `field` or `spec_field` structures that match the argument types exactly, ensuring format specifiers are validated against the actual types during compilation.

The parser composes these nodes into a **nested `concat` tree** (lines 44‑52) that represents the entire format string. Each node implements a `format(OutputIt, ...)` method, creating a linked structure where each element knows precisely how to output its segment.

## Core Components of the Compile API

### compiled_string and FMT_COMPILE

The `compiled_string` class (line 20) serves as a marker type indicating that a string literal should undergo compile-time parsing. The `FMT_COMPILE` macro forces literals through this compile path, bypassing the runtime parsing used by standard `fmt::format` calls.

### Field Representations

The compile API generates distinct node types for different format elements:
- **`text`**: Static character sequences extracted via `parse_text`
- **`code_unit`**: Single-character literals  
- **`field`**: Simple replacement fields without specifiers
- **`spec_field`**: Fields with compile-time parsed format specifications
- **`runtime_named_field`**: Named argument placeholders requiring runtime resolution

### The concat Structure

The `concat` template (lines 44‑52) implements a compile-time linked list that stitches formatting nodes together. Because all members are `constexpr`, the entire tree evaluates to constant expressions, allowing the compiler to optimize the formatting logic into inline output operations.

## Practical Usage Examples

### Basic Compile-Time Formatting

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

int main() {
    // Parsed entirely at compile time
    std::string s = fmt::format(FMT_COMPILE("The answer is {}"), 42);
    // Generates: "The answer is 42"
}

```

In this example, `FMT_COMPILE` creates a `compiled_string` type. The parser builds a `field<char, int, 0>` node for the `{}` placeholder, resolving the integer type at compile time. The resulting code writes the value directly without runtime format-string scanning.

### Format Specifiers at Compile Time

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

int main() {
    std::string s = fmt::format(FMT_COMPILE("{:04x}"), 255);
    // Generates: "00ff"
}

```

Here, `compile_format_string` encounters the `:` delimiter and invokes `parse_specs` to create a `spec_field<char, int, 0>`. This node stores a `formatter<int, char>` configured with the `04x` specification at compile time, generating code that performs hexadecimal conversion and zero-padding without runtime overhead.

### Named Arguments with C++20

```cpp
#include <fmt/compile.h>
using namespace fmt::literals;

int main() {
    auto fmt_str = "{val}"_cf;  // Compile-time literal operator
    std::string s = fmt::format(fmt_str, fmt::arg("val", 123));
}

```

When `FMT_USE_NONTYPE_TEMPLATE_ARGS` is enabled, the `_cf` literal operator forwards to `FMT_COMPILE`. The parser treats `{val}` as a named field, creating either a compile-time resolved field or a `runtime_named_field` that fetches the argument by name during execution while maintaining type safety.

## Summary

- **`FMT_COMPILE`** converts string literals into `compiled_string` types that trigger compile-time parsing in [`include/fmt/compile.h`](https://github.com/fmtlib/fmt/blob/main/include/fmt/compile.h).
- **`detail::compile_format_string`** implements a constexpr recursive parser (lines 82‑91) that walks format strings and builds node trees at compile time.
- **Node types** (`field`, `spec_field`, `text`, `concat`) represent parsed format elements as lightweight templates with zero runtime overhead.
- **Type resolution** occurs via `get_type` template metaprogramming (lines 15‑22), ensuring argument types match format specifiers during compilation.
- **Execution** happens through `fmt::format(const CompiledFormat&, ...)` (lines 76‑84), which writes output using pre-generated formatting logic without runtime string analysis.

## Frequently Asked Questions

### What is the performance benefit of using fmt::compile?

**`fmt::compile` eliminates runtime format string parsing entirely.** Because `detail::compile_format_string` executes at compile time, the resulting binary contains pre-computed formatting instructions rather than parsing loops. This reduces executable size for repeated format patterns and removes branching logic from hot paths, often resulting in formatting speeds comparable to handwritten code.

### Does fmt::compile work with runtime format strings?

**No, the compile API requires string literals known at compile time.** The `FMT_COMPILE` macro and `compiled_string` type depend on template metaprogramming and constexpr evaluation to parse the format string. For runtime strings, use the standard `fmt::format` API, which performs runtime parsing using the same underlying machinery in [`include/fmt/format.h`](https://github.com/fmtlib/fmt/blob/main/include/fmt/format.h).

### How does compile-time type safety work in the compile API?

**Type safety is enforced through template argument lists and `get_type` resolution.** When you call `fmt::format` with a `compiled_string`, the compiler deduces the argument types into a `type_list<T...>`. The `get_type<N, Args>` template (lines 15‑22) retrieves the Nth type at compile time, ensuring that `field` and `spec_field` nodes are instantiated only with compatible format specifications, catching type mismatches during compilation rather than at runtime.

### What C++ standard is required for fmt::compile?

**Full functionality requires C++17 or later.** The implementation relies on `if constexpr` for compile-time branching and improved return-type deduction to enable `FMT_COMPILE` expansion (lines 37‑40). While basic formatting works on older standards, the compile-time parsing and `compiled_string` optimizations require constexpr capabilities introduced in C++17, with additional features like non-type template parameter string literals available in C++20.