How `parse_format_specs` Validates Format Strings in fmtlib/fmt
The parse_format_specs function is a constexpr parser in fmt::detail that validates format specifiers appearing after the colon character, using an eight-stage state machine to enforce type-specific constraints and throwing fmt::format_error when specifications violate argument type rules.
The parse_format_specs function serves as the core validation engine for the {fmt} library, interpreting the portion of format strings that follows the colon (:) and transforming raw specifier text into structured format_specs objects. Implemented in include/fmt/core.h at line 1457, this constexpr function ensures that formatting options are semantically legal for the specific argument types provided, operating during both compile-time and runtime contexts.
State Machine Architecture
Located within the fmt::detail namespace, parse_format_specs implements a linear state machine that tracks progression through eight distinct stages: start, align, sign, hash, zero, width, precision, and locale. An internal enter_state lambda manages transitions between these stages, verifying that each new state occurs later in the sequence than the current one and that the transition is semantically valid for the argument type.
Fill Character and Alignment Detection
The parser first examines the initial character to determine whether it represents a fill character or a specifier token. If the look-ahead character matches an alignment token (<, >, ^), the current character becomes the fill character; otherwise, the parser treats it directly as a format specifier. The fill character must not be {, and alignment tokens trigger parse_align to set the alignment property in the format_specs object.
Sign, Alternate Form, and Zero Padding Validation
As the state machine progresses, it encounters optional sign indicators and formatting flags. The + character and space set sign::plus or sign::space respectively, while - indicates left alignment—these sign specifiers are restricted to arguments belonging to sint_set or float_set. The alternate form flag #, which invokes specs.set_alt(), and the zero-padding flag 0 are permitted only when is_arithmetic_type(arg_type) returns true, ensuring that these numeric formatting options apply exclusively to arithmetic types.
Width and Precision Parsing
Width specification handles numeric literals, dynamic width via {}, or positional width via {id}. The implementation delegates to parse_width, which internally calls parse_dynamic_spec to resolve runtime values from the argument list. Precision, triggered by a . character followed by a number or dynamic reference, undergoes stricter validation: it is legal only for types in float_set, string_set, or cstring_set, preventing precision specifications on incompatible integral types.
Locale and Presentation Type Enforcement
The L character enables locale-specific formatting but requires an arithmetic argument type. Presentation type characters—including d, x, X, o, b, B, e, E, f, F, g, G, a, A, c, s, p, and ?—map to specific presentation_type enum values. Each mapping validates that the argument type belongs to an appropriate type set; for example, integer presentations require integral_set membership, while string presentations require string_set or cstring_set membership.
Error Handling and Type Safety
When validation rules are violated, parse_format_specs invokes report_error("invalid format specifier"), which throws a fmt::format_error exception. This mechanism operates during both compile-time and runtime evaluation, ensuring that malformed specifiers trigger immediate failure rather than producing undefined behavior. The function returns only after successfully populating a format_specs object with fully validated parameters.
Practical Implementation Examples
The following examples demonstrate how parse_format_specs interprets various format specifications according to the validation rules:
#include <fmt/core.h>
#include <fmt/format.h>
int main() {
// Integer with hexadecimal presentation and zero-padding
fmt::print("{:08x}\n", 255); // → 000000ff
// Floating-point with locale-aware formatting and precision
fmt::print("{:L.2f}\n", 1234.5); // → 1,234.50 (locale-dependent)
// String with custom fill and center alignment
fmt::print("{:*^10}\n", "hi"); // → ***hi****
// Dynamic width specification
fmt::print("{:{}}", 42, 5); // → 42
}
These calls exercise the parser's handling of alignment flags, type-restricted specifiers, and dynamic argument resolution.
Summary
parse_format_specsis a constexpr function infmt::detaillocated ininclude/fmt/core.h(line 1457) that parses the colon-separated portion of format strings- It implements an eight-stage state machine (
start→align→sign→hash→zero→width→precision→locale) via anenter_statelambda to enforce ordering constraints - Type validation restricts sign specifiers to numeric types (
sint_set,float_set), precision to floats and strings (float_set,string_set,cstring_set), and alternate form to arithmetic types - Errors trigger
fmt::format_errorthrough thereport_errormechanism, providing early failure for malformed input - The function supports both compile-time and runtime format string validation, enabling static analysis of format correctness
Frequently Asked Questions
What triggers a format_error in parse_format_specs?
Invalid state transitions, type mismatches (such as specifying precision on an integer), malformed dynamic width/precision syntax, or unrecognized presentation types invoke report_error, which throws fmt::format_error to halt formatting immediately before producing output.
Can parse_format_specs evaluate format strings at compile time?
Yes. The function is declared constexpr, enabling compile-time validation of format specifiers when used with FMT_STRING or other compile-time string contexts, allowing the compiler to catch format errors during compilation rather than at runtime.
Which types support precision specifiers according to parse_format_specs?
The function permits precision only for floating-point types (members of float_set), standard strings (string_set), and C-strings (cstring_set), explicitly rejecting precision for integral types, pointers, and other non-applicable argument categories.
How does parse_format_specs differentiate between fill characters and specifiers?
The parser performs look-ahead analysis: if the character following the potential fill character is an alignment token (<, >, or ^), the first character is treated as a custom fill character; otherwise, the parser assumes the first character is itself a format specifier or alignment flag.
Have a question about this repo?
These articles cover the highlights, but your codebase questions are specific. Give your agent direct access to the source. Share this with your agent to get started:
curl -s "https://instagit.com/install.md" Maintain an open-source project? Get it listed too →