Skip to content

support fill, alignment and width in range and tuple formatters - #4910

Merged
vitaut merged 1 commit into
fmtlib:mainfrom
avionicharshit-byte:range-tuple-width
Sep 6, 2026
Merged

support fill, alignment and width in range and tuple formatters#4910
vitaut merged 1 commit into
fmtlib:mainfrom
avionicharshit-byte:range-tuple-width

Conversation

@avionicharshit-byte

Copy link
Copy Markdown
Contributor

fmt::format("{:*>20}", std::vector<int>{1,2,3}) doesnt compile , it fails with "invalid format specifier" . same for tuple , pair and map . std::format accepts it .

// before
fmt::format("{:*>20}", std::vector<int>{1, 2, 3});  // error: invalid format specifier

// after
fmt::format("{:*>20}", std::vector<int>{1, 2, 3});  // "***********[1, 2, 3]"

in C++23 both range-format-spec and tuple-format-spec start with fill-and-align_opt width_opt , so fill , alignment and width belong to the composed output . range_formatter::parse never called parse_align or parse_width , it went straight to looking for n , s and ? . doc/syntax.md documented the shorter grammar as well , so thats updated too .

the fix adds detail::composed_specs in include/fmt/ranges.h , used from the range , tuple and map formatters . it parses alignment and width before the range's own options , and skips a leading : so {::>5} still means the underlying element spec , which is what std::format does . in format() , when a width is asked for the composed output goes into a memory_buffer first and is then written padded . thats the same buffer and pad idiom nested_formatter uses , and the same one #4860 used for the std::exception formatter .

each formatter's old body moved into write_body unchanged and format() became the padding wrapper . nothing existing changed behaviour , no existing test expectation was edited .

i checked 22 cases against real std::format output from libc++ : default align , < > ^ , custom fill , dynamic width {:*>{}} , width with n , width with a nested element spec , sets , maps , vector<std::string> debug quoting , tuple , pair , and output longer than the width . 21 of the 22 match byte for byte . added ranges_test.format_width covering them , all 22 test suites pass , clang-format clean .

the 22nd is not part of this PR . fmt::format("{:n}", std::pair<int, int>{1, 2}) gives 12 where std::format gives 1, 2 , fmt drops the separator along with the brackets for tuples but keeps it for ranges . thats already on master and i havent touched it , fixing it would change the expectations at test/ranges-test.cc:176 and :183 . happy to file it separately .

@avionicharshit-byte avionicharshit-byte changed the title Support fill, alignment and width in range and tuple formatters support fill, alignment and width in range and tuple formatters Sep 2, 2026
@avionicharshit-byte
avionicharshit-byte marked this pull request as ready for review September 2, 2026 18:06
@avionicharshit-byte

Copy link
Copy Markdown
Contributor Author

@vitaut ci is waiting on workflow approval. the earlier failure was a missing FMT_CONSTEXPR on composed_specs::resolve, fixed and amended.

@vitaut vitaut left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Thanks for the PR! Overall looks good but please update the doc per inline comment.

Comment thread doc/syntax.md Outdated
@vitaut
vitaut merged commit 61e3b92 into fmtlib:main Sep 6, 2026
46 of 47 checks passed
@vitaut

vitaut commented Sep 6, 2026

Copy link
Copy Markdown
Contributor

Merged, thanks!

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants