Skip to content

Fix format_as for tuple-like types with ranges.h - #4945

Merged
vitaut merged 7 commits into
fmtlib:mainfrom
dajiaohuang:fix-tuple-format-as-ranges-4944
Sep 18, 2026
Merged

vitaut merged 7 commits into
fmtlib:mainfrom
dajiaohuang:fix-tuple-format-as-ranges-4944

Conversation

@dajiaohuang

@dajiaohuang dajiaohuang commented Sep 13, 2026 •

Copy link
Copy Markdown
Contributor

Fixes #4944

A tuple-like type can also provide an ADL format_as function. The tuple formattability probe should defer to that formatter instead of recursively checking tuple elements.

This change detects format_as before probing tuple elements, so the existing format_as formatter takes precedence.

Tests:

  • Reproducer compile with MSVC /std:c++20
  • Full CMake/CTest suite (22 tests)

@dajiaohuang
dajiaohuang requested a review from vitaut as a code owner September 13, 2026 15:39

@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. I think format_as should indeed take precedence over tuple formatting, but I’d prefer not to change the semantics of is_tuple_formattable.

fmt::is_tuple_formattable is an exported trait and currently answers whether the tuple elements are formattable. With this change it becomes false for every tuple-like type that has format_as, even if all of its elements are formattable. Could we instead keep is_tuple_formattable_ unchanged and exclude format_as when selecting the tuple formatter, while still making sure the tuple formattability check is not instantiated in that case?

Nit: could you also replace the \n escapes in the PR description with actual newlines? The current description is a bit odd to read/edit.

@dajiaohuang

dajiaohuang commented Sep 16, 2026 •

Copy link
Copy Markdown
Contributor Author

Updated in 4d5286b (amended after CI showed the public tuple-formattability value is intentionally unchanged for this recursive tuple-like test type).

  • Restored fmt::is_tuple_formattable to its existing tuple-element semantics.
  • Added a formatter-only trait that skips the tuple probe when ADL format_as is available, so the dedicated format_as formatter still takes precedence without instantiating the recursive check.
  • Kept the existing runtime regression test for a tuple-like format_as type.
  • Rewrote the PR description with actual newlines.

Validation: git diff --check passed. A local CMake/compiler toolchain is not installed in this workspace; the repository CI will provide the full build/test validation.

@dajiaohuang
dajiaohuang force-pushed the fix-tuple-format-as-ranges-4944 branch from e8b75bc to d3419f1 Compare September 16, 2026 16:04
@dajiaohuang
dajiaohuang force-pushed the fix-tuple-format-as-ranges-4944 branch from d3419f1 to 4d5286b Compare September 16, 2026 16:10
@dajiaohuang

Copy link
Copy Markdown
Contributor Author

Implemented and pushed commit 57dc090. This preserves the public fmt::is_tuple_formattable trait semantics while keeping formatter dispatch excluded for format_as tuple-like types, and adds a regression assertion covering the distinction. git diff --check passes. Local CMake/CTest are unavailable in this environment, so please rely on the remote CI for the full test suite.

@dajiaohuang

Copy link
Copy Markdown
Contributor Author

Follow-up: corrected the regression assertion to pass the required char parameter to the exported fmt::is_tuple_formattable<T, C> trait (commit 13e3aa7). The preceding CI failure was only this test compile error; the fix is now pushed. Remote CI should rerun.

@dajiaohuang

Copy link
Copy Markdown
Contributor Author

Follow-up: the exported trait assertion now uses static_assert (commit c462af2), avoiding C++11/14 ODR-use linker errors while still verifying fmt::is_tuple_formattable<format_as_tuple_like, char>::value remains true. The prior CI failures were limited to that assertion form.

@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.

Mostly looks good, just one more inline comment.

Comment thread include/fmt/ranges.h Outdated
Comment on lines +167 to +170
template <typename T, typename = void>
struct has_format_as_ : std::false_type {};
template <typename T>
struct has_format_as_<T, void_t<format_as_result<T>>> : std::true_type {};

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.

This can be folded into is_tuple_formattable_for_formatter.

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

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

Folded the format_as detection into is_tuple_formattable_for_formatter so the helper directly defaults to the exported tuple trait and specializes to false only when format_as_result<T> is valid. Pushed as 83c869a; git diff --check passes.

@vitaut
vitaut merged commit fd0a9b6 into fmtlib:main Sep 18, 2026
47 checks passed
@vitaut

vitaut commented Sep 18, 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.

Tuple-like type becomes unformattable if <fmt/ranges.h> is included

2 participants