# fmtlib Type Enumeration: How {fmt} Maps C++ Types to Compile-Time Formatters

> Understand the fmtlib type enumeration. Learn how fmt maps C++ types to compile-time formatters for zero-overhead, type-safe formatting without RTTI.

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

---

**The `fmt::type` enumeration in [`include/fmt/core.h`](https://github.com/fmtlib/fmt/blob/main/include/fmt/core.h) classifies every format argument into a compact set of categories, enabling zero-overhead dispatch and type-safe formatting without RTTI.**

The {fmt} library achieves its renowned performance and type safety through a carefully designed type system centered on `enum class type`. This enumeration, defined at lines 73-94 of [`include/fmt/core.h`](https://github.com/fmtlib/fmt/blob/main/include/fmt/core.h), serves as the foundation for how the library understands, stores, and processes format arguments at both compile time and run time.

## What Is the fmtlib Type Enumeration?

The `type` enumeration lists every fundamental argument category the library can format:

| Enumerator | Represents | Typical C++ Types |
|------------|-----------|-----------------|
| `none_type` | Placeholder / no value | — |
| `int_type` | Signed integers | `int` |
| `uint_type` | Unsigned integers | `unsigned` |
| `long_long_type` | 64-bit signed integers | `long long` |
| `ulong_long_type` | 64-bit unsigned integers | `unsigned long long` |
| `int128_type` / `uint128_type` | 128-bit integers (platform-dependent) | `__int128` / `unsigned __int128` |
| `bool_type` | Boolean values | `bool` |
| `char_type` | Character code units | `char`, `wchar_t`, `char8_t`, `char16_t`, `char32_t` |
| `float_type` | Single-precision floats | `float` |
| `double_type` | Double-precision floats | `double` |
| `long_double_type` | Extended-precision floats | `long double` |
| `cstring_type` | C-style null-terminated strings | `const char*`, `const wchar_t*`, etc. |
| `string_type` | String views | `basic_string_view<Char>` |
| `pointer_type` | Raw pointers | `const void*` |
| `custom_type` | User-defined types with formatters | Any type with `fmt::formatter<T>` specialization |

This compact representation—typically fitting in a single byte—allows {fmt} to handle type information with minimal memory overhead and zero runtime type identification (RTTI) costs.

## Three Core Purposes of the Type Enumeration

### 1. Compile-Time Type Dispatch

The `type_constant<T, Char>` template maps concrete C++ types to their corresponding `type` enumerators. This mapping happens entirely at compile time.

When you write `fmt::print("{}", 42)`, the library resolves `type_constant<int, char>` to `type::int_type`. This compile-time resolution eliminates virtual function calls and enables aggressive inlining for hot formatting paths.

### 2. Fast Type Category Testing

Helper bit-set constants defined around lines 1035-1045 in [`include/fmt/core.h`](https://github.com/fmtlib/fmt/blob/main/include/fmt/core.h) use the enum values to build compile-time masks:

- `sint_set` — signed integer types (`int_type`, `long_long_type`, `int128_type`)
- `uint_set` — unsigned integer types (`uint_type`, `ulong_long_type`, `uint128_type`)
- `float_set` — floating-point types (`float_type`, `double_type`, `long_double_type`)
- `integral_set` — all integer types
- `arithmetic_set` — all numeric types

These bit sets power functions like `is_integral_type(type t)` and `is_arithmetic_type(type t)`, enabling single-instruction type category checks.

### 3. Runtime Argument List Representation

When formatting variadic templates, each argument's type is stored as a `type` value in the internal argument list. This compact metadata allows the library to:

- Iterate over heterogeneous argument sequences
- Validate format specifiers against actual types (e.g., rejecting `%f` for `int_type`)
- Select the correct formatting implementation without type erasure penalties

## Practical Code Examples

### Basic Type Mapping

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

int main() {
    // Automatically maps to type::int_type
    fmt::print("Integer: {}\n", 42);
    
    // Automatically maps to type::double_type
    fmt::print("Float: {}\n", 3.14159);
    
    // Automatically maps to type::string_type
    fmt::print("String: {}\n", std::string_view("hello"));
}

```

### Custom Type Classification

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

struct Point { 
    int x, y; 
};

// Custom formatter specialization
template<> 
struct fmt::formatter<Point> {
    constexpr auto parse(fmt::format_parse_context& ctx) {
        return ctx.begin();
    }
    
    template<typename FormatContext>
    auto format(const Point& p, FormatContext& ctx) const {
        return fmt::format_to(ctx.out(), "({}, {})", p.x, p.y);
    }
};

int main() {
    // Point is classified as type::custom_type
    fmt::print("Point: {}\n", Point{1, 2});
}

```

In this example, `Point` lacks a built-in `type_constant` mapping, so the library routes it through `type::custom_type` and invokes the user-provided `formatter<Point>` specialization.

## Key Implementation Files

- **[`include/fmt/core.h`](https://github.com/fmtlib/fmt/blob/main/include/fmt/core.h)** — Definition of `enum class type`, `type_constant` mapping, and bit-set utilities
- **[`include/fmt/format.h`](https://github.com/fmtlib/fmt/blob/main/include/fmt/format.h)** — Formatter dispatch logic that consumes `type` values for compile-time checks
- **[`include/fmt/args.h`](https://github.com/fmtlib/fmt/blob/main/include/fmt/args.h)** — Runtime argument storage using `type` enum in variadic argument lists
- **[`include/fmt/enum.h`](https://github.com/fmtlib/fmt/blob/main/include/fmt/enum.h)** — Extended enum formatting support (separate from the core type system)

## Design Trade-offs and Performance Characteristics

The `type` enumeration reflects deliberate engineering decisions:

- **Fixed enumeration vs. open type system** — User-defined types must provide `formatter<T>` specializations; there is no dynamic registration
- **No RTTI dependency** — All type information is either compile-time constant or explicitly stored, avoiding `typeid` and `dynamic_cast` overhead
- **Platform-conditional members** — `int128_type` and `uint128_type` appear only when `__int128` is supported by the compiler

These constraints enable {fmt} to format arguments with throughput approaching manual `std::to_string` calls while maintaining full type safety.

## Summary

- The `fmt::type` enumeration in [`include/fmt/core.h`](https://github.com/fmtlib/fmt/blob/main/include/fmt/core.h) categorizes all formatable values into 15+ fundamental types
- **Compile-time dispatch** via `type_constant<T, Char>` eliminates virtual calls and enables inlining
- **Bit-set constants** built from enum values provide single-instruction type category testing
- **Runtime storage** of `type` values enables efficient variadic argument processing without RTTI
- User-defined types automatically map to `custom_type` when `formatter<T>` specializations are provided

## Frequently Asked Questions

### How does {fmt} handle types not in the enum?

Types without built-in mappings are classified as `type::custom_type`. The library requires a `fmt::formatter<T>` specialization for such types, which it invokes through the custom type dispatch path. No runtime registration or dynamic type discovery is supported.

### Is there overhead from storing type information at runtime?

No meaningful overhead. Each argument stores one byte (or less) of `type` metadata. This is comparable to manual variadic template expansion and far cheaper than `std::any` or virtual function approaches used by some alternative formatting libraries.

### Why use an enum instead of template specializations alone?

The enum enables **uniform runtime representation** for type checking and dispatch. While template specializations handle the actual formatting, the `type` value allows format string validation and argument iteration to work efficiently across heterogeneous parameter packs without instantiating per-type code paths for control flow.

### Does the enumeration vary across platforms?

Yes, minimally. The `int128_type` and `uint128_type` enumerators are conditionally defined based on compiler support for 128-bit integers. All other enumerators are stable across platforms and character types (`char`, `wchar_t`, etc.).