From 9d848ee4d33181bf4c7a1c2cf5dff31f2b9b16fe Mon Sep 17 00:00:00 2001 From: Justin Riddell Date: Wed, 15 May 2024 03:47:03 +0100 Subject: [PATCH] Disable range formatter when user provides one Disable formatting type as range if user provided format_as function exists, using that instead. Should fix issue raised in #3839 --- include/fmt/ranges.h | 1 + test/ranges-test.cc | 62 ++++++++++++++++++++++++++++++++++++++++++++ 2 files changed, 63 insertions(+) diff --git a/include/fmt/ranges.h b/include/fmt/ranges.h index 3a37c772a34b..4e490d178e83 100644 --- a/include/fmt/ranges.h +++ b/include/fmt/ranges.h @@ -513,6 +513,7 @@ template struct formatter< R, Char, enable_if_t{}>, bool_constant::value != range_format::disabled && range_format_kind::value != range_format::map> diff --git a/test/ranges-test.cc b/test/ranges-test.cc index 8ab6c60eba0d..3fe4ea3f6996 100644 --- a/test/ranges-test.cc +++ b/test/ranges-test.cc @@ -7,6 +7,7 @@ #include "fmt/ranges.h" +#include #include #include #include @@ -742,3 +743,64 @@ TEST(ranges_test, movable_only_istream_iter_join) { EXPECT_EQ("1, 2, 3, 4, 5", fmt::format("{}", fmt::join(std::move(first), last, ", "))); } + +namespace ranges_format_as { +struct RangeLike { + std::array arr = {{'h', 'e', 'y'}}; + using iter = decltype(arr)::const_iterator; + iter begin() const { return arr.begin(); } + iter end() const { return arr.end(); } +}; + +struct RangeLikeWithFormatAs : RangeLike { + friend std::string format_as(const RangeLikeWithFormatAs& r) { + std::string s{r.begin(), r.end()}; + s += '!'; + return s; + } +}; + +struct RangeLikeSpecialized : RangeLike {}; +} // namespace ranges_format_as + +FMT_BEGIN_NAMESPACE +template <> +struct formatter + : formatter { + auto format(const ranges_format_as::RangeLikeSpecialized& r, + format_context& ctx) const -> format_context::iterator { + std::string s{r.begin(), r.end()}; + std::reverse(s.begin(), s.end()); + return formatter::format(s, ctx); + } +}; +FMT_END_NAMESPACE + +TEST(ranges_test, prefer_user_formatter_to_range_like) { + using namespace ranges_format_as; + { + EXPECT_EQ(fmt::format("{}", RangeLike{}), "['h', 'e', 'y']"); + using T = RangeLike; + constexpr std::array arr{T{}, T{}, T{}}; + EXPECT_EQ(fmt::format("{}", arr), + "[['h', 'e', 'y'], ['h', 'e', 'y'], ['h', 'e', 'y']]"); + EXPECT_EQ(fmt::format("{}", fmt::join(arr, " - ")), + "['h', 'e', 'y'] - ['h', 'e', 'y'] - ['h', 'e', 'y']"); + } + + { + EXPECT_EQ(fmt::format("{}", RangeLikeWithFormatAs{}), "hey!"); + using T = RangeLikeWithFormatAs; + constexpr std::array arr{T{}, T{}, T{}}; + EXPECT_EQ(fmt::format("{}", arr), "[\"hey!\", \"hey!\", \"hey!\"]"); + EXPECT_EQ(fmt::format("{}", fmt::join(arr, " - ")), "hey! - hey! - hey!"); + } + + { + EXPECT_EQ(fmt::format("{}", RangeLikeSpecialized{}), "yeh"); + using T = RangeLikeSpecialized; + constexpr std::array arr{T{}, T{}, T{}}; + EXPECT_EQ(fmt::format("{}", arr), "[\"yeh\", \"yeh\", \"yeh\"]"); + EXPECT_EQ(fmt::format("{}", fmt::join(arr, " - ")), "yeh - yeh - yeh"); + } +}