From 63ebcda94989cd1abeff342702bf2b405d73888e Mon Sep 17 00:00:00 2001 From: dajiaohuang Date: Sun, 13 Sep 2026 23:39:29 +0800 Subject: [PATCH 1/3] Fix format_as for tuple-like types --- include/fmt/ranges.h | 7 ++++++- test/ranges-test.cc | 17 +++++++++++++++++ 2 files changed, 23 insertions(+), 1 deletion(-) diff --git a/include/fmt/ranges.h b/include/fmt/ranges.h index 678df0f1032e..b4fd20f034b9 100644 --- a/include/fmt/ranges.h +++ b/include/fmt/ranges.h @@ -164,7 +164,12 @@ using make_index_sequence = make_integer_sequence; template using tuple_index_sequence = make_index_sequence::value>; -template ::value> +template struct has_format_as_ : std::false_type {}; +template +struct has_format_as_>> : std::true_type {}; + +template ::value && !has_format_as_::value> class is_tuple_formattable_ { public: static constexpr bool value = false; diff --git a/test/ranges-test.cc b/test/ranges-test.cc index d3ca6498faac..9cdbb2019514 100644 --- a/test/ranges-test.cc +++ b/test/ranges-test.cc @@ -721,6 +721,23 @@ TEST(ranges_test, format_as_tie) { EXPECT_EQ(fmt::format("{}", tieable()), "(3, 0.42)"); } +struct format_as_tuple_like {}; + +auto format_as(const format_as_tuple_like&) -> std::string { return "Hello"; } + +namespace std { +template <> struct tuple_size + : integral_constant {}; +template <> struct tuple_element<0, format_as_tuple_like> { + using type = format_as_tuple_like; +}; +} // namespace std + +TEST(ranges_test, format_as_tuple_like) { + EXPECT_TRUE((fmt::is_formattable::value)); + EXPECT_EQ(fmt::format("{}", format_as_tuple_like{}), "Hello"); +} + struct lvalue_qualified_begin_end { int arr[5] = {1, 2, 3, 4, 5}; From 7881fc217b9a8d389cb2d022943b354c5dec09f6 Mon Sep 17 00:00:00 2001 From: dajiaohuang Date: Sun, 13 Sep 2026 23:43:42 +0800 Subject: [PATCH 2/3] Format format_as tuple trait declaration --- include/fmt/ranges.h | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/include/fmt/ranges.h b/include/fmt/ranges.h index b4fd20f034b9..cfff028f93e8 100644 --- a/include/fmt/ranges.h +++ b/include/fmt/ranges.h @@ -164,7 +164,8 @@ using make_index_sequence = make_integer_sequence; template using tuple_index_sequence = make_index_sequence::value>; -template struct has_format_as_ : std::false_type {}; +template +struct has_format_as_ : std::false_type {}; template struct has_format_as_>> : std::true_type {}; From 4d5286b435ab8518c48aa92bd5bb86cd374163aa Mon Sep 17 00:00:00 2001 From: dajiaohuang Date: Thu, 17 Sep 2026 00:01:13 +0800 Subject: [PATCH 3/3] Fix tuple formattability with format_as --- include/fmt/ranges.h | 12 +++++++++++- 1 file changed, 11 insertions(+), 1 deletion(-) diff --git a/include/fmt/ranges.h b/include/fmt/ranges.h index cfff028f93e8..b40a68dfe521 100644 --- a/include/fmt/ranges.h +++ b/include/fmt/ranges.h @@ -303,10 +303,20 @@ template struct is_tuple_formattable { static constexpr bool value = detail::is_tuple_formattable_::value; }; +namespace detail { +template ::value> +struct is_tuple_formattable_for_formatter : std::false_type {}; + +template +struct is_tuple_formattable_for_formatter + : std::integral_constant::value> {}; +} // namespace detail + template struct formatter::value && - fmt::is_tuple_formattable::value>> { + detail::is_tuple_formattable_for_formatter< + Tuple, Char>::value>> { private: decltype(detail::tuple::get_formatters( detail::tuple_index_sequence())) formatters_;