# How to Check Format Strings at Compile Time Using FMT_STRING in {fmt}

> Learn how to check format strings at compile time using FMT_STRING in fmtlibfmt. Catch type mismatches and invalid specifiers early with this powerful macro.

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

---

**Wrap your format string literals with the `FMT_STRING` macro to enable compile-time validation that catches type mismatches and invalid specifiers before your program runs.**

The {fmt} library provides powerful string formatting capabilities, but runtime format errors can still crash production applications. By leveraging `FMT_STRING` from the fmtlib/fmt repository, you can validate format specifiers against argument types during compilation. This approach uses template metaprogramming to shift error detection from runtime to build time.

## How FMT_STRING Implements Compile-Time Checking

### The Macro Expansion Mechanism

According to the fmtlib/fmt source code in [`include/fmt/format.h`](https://github.com/fmtlib/fmt/blob/main/include/fmt/format.h) (lines 4476-4504), the `FMT_STRING` macro expands to `FMT_STRING_IMPL(s, fmt::detail::compile_string)`. This implementation employs a lambda trick to generate a hidden-visibility structure that inherits from `compile_string`. The struct provides an `operator basic_string_view` that converts the literal to a view at compile time via `compile_string_to_view`.

### Static Assertion Validation

When you pass a `FMT_STRING`-wrapped literal to `fmt::format`, the function receives it as a `format_string<T...>` parameter. This template triggers `static_assert` statements that verify each conversion specifier against the supplied argument types. If you attempt to pass a string to a `{:d}` numeric specifier, the compiler emits an error immediately rather than throwing a runtime exception.

## Practical Implementation Examples

### Basic Compile-Time Validation

The following example demonstrates valid usage and the compile-time error triggered by a type mismatch:

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

int main() {
    // Valid: "{}" accepts any type
    std::string s = fmt::format(FMT_STRING("{}"), 42);
    
    // Invalid: "{:d}" requires an integer argument
    // std::string bad = fmt::format(FMT_STRING("{:d}"), "hello"); 
    // Error: static assertion failed: format string uses numeric specifier with non-numeric type
}

```

### Wide Character Support

`FMT_STRING` works with wide string literals using the standard `L` prefix:

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

int main() {
    std::wstring ws = fmt::format(FMT_STRING(L"{:04x}"), 255);
    // Result: "00ff"
}

```

## FMT_COMPILE and Zero Runtime Overhead

For performance-critical code, `FMT_COMPILE` (declared in [`include/fmt/compile.h`](https://github.com/fmtlib/fmt/blob/main/include/fmt/compile.h) at lines 37-41) eliminates the runtime parser entirely. You can also use the `_cf` user-defined literal from the `fmt::literals` namespace:

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

using namespace fmt::literals;

int main() {
    // Full compile-time parsing with zero runtime overhead
    auto s = fmt::format("{}"_cf, 3.14);
    
    // Equivalent explicit macro usage
    auto t = fmt::format(FMT_COMPILE("{}"), 3.14);
}

```

Both approaches parse the format string during compilation, generating efficient formatting code that rivals hand-written alternatives.

## When to Use Compile-Time Format Checking

Apply `FMT_STRING` in these scenarios:

- **Safety-critical systems** where format string vulnerabilities must be eliminated at build time rather than caught in production
- **Performance-critical paths** where `FMT_COMPILE` removes all runtime parsing overhead while maintaining type safety
- **Gradual code modernization** allowing you to add compile-time checks to specific calls without refactoring your entire codebase to use `FMT_COMPILE` immediately

## Summary

- **`FMT_STRING`** wraps string literals to enable compile-time validation, defined in [`include/fmt/format.h`](https://github.com/fmtlib/fmt/blob/main/include/fmt/format.h)
- The macro creates a `compile_string` type that triggers `static_assert` checks during template instantiation in `format_string<T...>`
- **`FMT_COMPILE`** provides zero-overhead formatting by parsing entirely at compile time in [`include/fmt/compile.h`](https://github.com/fmtlib/fmt/blob/main/include/fmt/compile.h)
- Both mechanisms support wide character strings (`L"..."`) and integrate seamlessly with existing `fmt::format` calls
- Invalid specifiers produce clear compiler errors rather than runtime `format_error` exceptions

## Frequently Asked Questions

### What is the difference between FMT_STRING and FMT_COMPILE?

`FMT_STRING` validates format strings at compile time but may retain some runtime parsing infrastructure depending on the compiler optimization context. `FMT_COMPILE`, defined in [`include/fmt/compile.h`](https://github.com/fmtlib/fmt/blob/main/include/fmt/compile.h), generates formatting code entirely at compile time, eliminating all runtime overhead. Use `FMT_STRING` for safety validation with familiar syntax, and `FMT_COMPILE` when you require maximum performance.

### Does FMT_STRING add runtime overhead to my application?

No. Because `FMT_STRING` performs validation during template instantiation, all checks occur at compile time. The `static_assert` mechanisms in `fmt::detail::compile_string` execute during compilation, and the resulting machine code contains no additional branches or memory overhead compared to runtime-checked format calls.

### Can I use FMT_STRING with wide strings and custom types?

Yes. `FMT_STRING` supports wide character literals when prefixed with `L`, such as `FMT_STRING(L"{:04x}")`. The library processes these through the same `compile_string` mechanism as narrow strings. For custom types, compile-time checking validates that your type provides the required formatter specialization before the code compiles successfully.

### Where is FMT_STRING defined in the fmt source code?

The macro is defined in [`include/fmt/format.h`](https://github.com/fmtlib/fmt/blob/main/include/fmt/format.h) between lines 4476 and 4504. It relies on `fmt::detail::compile_string` and the `compile_string_to_view` conversion function to create compile-time format string views. These views trigger static type checking when passed to `fmt::format` overloads accepting `format_string<T...>` parameters.