From 794df69c8c5fddb27742d2dce9adf3c61dd1dd21 Mon Sep 17 00:00:00 2001 From: Matthias Moulin Date: Fri, 14 Jun 2024 00:43:29 +0200 Subject: [PATCH] Added range_format::(debug_)string formatter (#3973) --- include/fmt/ranges.h | 55 +++++++++++++++++++++++++++++++++++++++++--- test/ranges-test.cc | 27 ++++++++++++++++++++++ 2 files changed, 79 insertions(+), 3 deletions(-) diff --git a/include/fmt/ranges.h b/include/fmt/ranges.h index 49eede2a..fd92ad9c 100644 --- a/include/fmt/ranges.h +++ b/include/fmt/ranges.h @@ -11,9 +11,13 @@ #ifndef FMT_IMPORT_STD # include # include +# include # include # include # include +# ifdef __cpp_lib_ranges +# include +# endif #endif #include "format.h" @@ -516,9 +520,11 @@ template struct formatter< R, Char, enable_if_t::value != - range_format::disabled && - range_format_kind::value != range_format::map> + bool_constant< + range_format_kind::value != range_format::disabled && + range_format_kind::value != range_format::map && + range_format_kind::value != range_format::string && + range_format_kind::value != range_format::debug_string> // Workaround a bug in MSVC 2015 and earlier. #if !FMT_MSC_VERSION || FMT_MSC_VERSION >= 1910 , @@ -607,6 +613,49 @@ struct formatter< } }; +// A (debug_)string formatter. +template +struct formatter< + R, Char, + enable_if_t::value == range_format::string || + range_format_kind::value == + range_format::debug_string>> { + private: + using range_type = detail::maybe_const_range; +#ifdef __cpp_lib_ranges + using string_type = conditional_t< + std::is_constructible_v, + std::ranges::iterator_t, + std::ranges::sentinel_t>, + std::basic_string_view, std::basic_string>; +#else + using string_type = std::basic_string; +#endif + + formatter underlying_; + + public: + template + FMT_CONSTEXPR auto parse(ParseContext& ctx) -> decltype(ctx.begin()) { + return underlying_.parse(ctx); + } + + template + auto format(range_type& range, FormatContext& ctx) const + -> decltype(ctx.out()) { + auto out = ctx.out(); + if (detail::const_check(range_format_kind::value == + range_format::debug_string)) + *out++ = '"'; + out = underlying_.format( + string_type{detail::range_begin(range), detail::range_end(range)}, ctx); + if (detail::const_check(range_format_kind::value == + range_format::debug_string)) + *out++ = '"'; + return out; + } +}; + template struct join_view : detail::view { It begin; diff --git a/test/ranges-test.cc b/test/ranges-test.cc index e05ae148..33ac5e38 100644 --- a/test/ranges-test.cc +++ b/test/ranges-test.cc @@ -268,6 +268,33 @@ TEST(ranges_test, disabled_range_formatting_of_path) { fmt::range_format::disabled); } +struct vector_string : std::vector { + using base = std::vector; + using base::base; +}; +struct vector_debug_string : std::vector { + using base = std::vector; + using base::base; +}; +FMT_BEGIN_NAMESPACE +template <> +struct range_format_kind + : std::integral_constant {}; +template <> +struct range_format_kind + : std::integral_constant {}; +FMT_END_NAMESPACE + +TEST(ranges_test, range_format_string) { + const vector_string v{'f', 'o', 'o'}; + EXPECT_EQ(fmt::format("{}", v), "foo"); +} + +TEST(ranges_test, range_format_debug_string) { + const vector_debug_string v{'f', 'o', 'o'}; + EXPECT_EQ(fmt::format("{}", v), "\"foo\""); +} + // A range that provides non-const only begin()/end() to test fmt::join // handles that. //