From a4d947cef00b466f93ed877cea8e556d08a6c359 Mon Sep 17 00:00:00 2001 From: Matthias Moulin Date: Mon, 27 May 2024 21:15:14 +0200 Subject: [PATCH] Added range_format_string and range_format_debug_string formatters --- include/fmt/ranges.h | 89 +++++++++++++++++++++++++++++++++----------- test/ranges-test.cc | 29 +++++++++++++++ 2 files changed, 97 insertions(+), 21 deletions(-) diff --git a/include/fmt/ranges.h b/include/fmt/ranges.h index efb37f28f6ca5..ca89258144d0d 100644 --- a/include/fmt/ranges.h +++ b/include/fmt/ranges.h @@ -14,6 +14,9 @@ # include # include # include +# ifdef __cpp_lib_ranges +# include +# endif #endif #include "format.h" @@ -171,11 +174,12 @@ template class is_tuple_formattable_ { static auto all_true(...) -> std::false_type; template - static auto check(index_sequence) -> decltype(all_true( - index_sequence{}, - integer_sequence::type, - C>::value)...>{})); + static auto check(index_sequence) + -> decltype(all_true( + index_sequence{}, + integer_sequence< + bool, (is_formattable::type, + C>::value)...>{})); public: static constexpr const bool value = @@ -335,8 +339,8 @@ struct formatter - auto format(const Tuple& value, FormatContext& ctx) const - -> decltype(ctx.out()) { + auto format(const Tuple& value, + FormatContext& ctx) const -> decltype(ctx.out()) { ctx.advance_to(detail::copy(opening_bracket_, ctx.out())); detail::for_each2( formatters_, value, @@ -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 , @@ -544,8 +550,8 @@ struct formatter< } template - auto format(range_type& range, FormatContext& ctx) const - -> decltype(ctx.out()) { + auto format(range_type& range, + FormatContext& ctx) const -> decltype(ctx.out()) { return range_formatter_.format(range, ctx); } }; @@ -607,6 +613,47 @@ struct formatter< } }; +#ifdef __cpp_lib_ranges +// 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; + using string_type = conditional_t< + std::is_constructible_v, + std::ranges::iterator_t, + std::ranges::sentinel_t>, + std::basic_string_view, std::basic_string>; + + 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 constexpr (range_format_kind::value == + range_format::debug_string) + *out++ = '\"'; + out = underlying_.format( + string_type{std::ranges::begin(range), std::ranges::end(range)}, ctx); + if constexpr (range_format_kind::value == + range_format::debug_string) + *out++ = '\"'; + return out; + } +}; +#endif + template struct join_view : detail::view { It begin; @@ -641,8 +688,8 @@ struct formatter, Char> { } template - auto format(view_ref& value, FormatContext& ctx) const - -> decltype(ctx.out()) { + auto format(view_ref& value, + FormatContext& ctx) const -> decltype(ctx.out()) { auto it = std::forward(value).begin; auto out = ctx.out(); if (it == value.end) return out; @@ -684,9 +731,9 @@ auto join(It begin, Sentinel end, string_view sep) -> join_view { \endrst */ template -auto join(Range&& r, string_view sep) - -> join_view { +auto join(Range&& r, + string_view sep) -> join_view { return {detail::range_begin(r), detail::range_end(r), sep}; } @@ -817,8 +864,8 @@ FMT_BEGIN_EXPORT \endrst */ template -FMT_CONSTEXPR auto join(const std::tuple& tuple, string_view sep) - -> tuple_join_view { +FMT_CONSTEXPR auto join(const std::tuple& tuple, + string_view sep) -> tuple_join_view { return {tuple, sep}; } @@ -834,8 +881,8 @@ FMT_CONSTEXPR auto join(const std::tuple& tuple, string_view sep) \endrst */ template -auto join(std::initializer_list list, string_view sep) - -> join_view { +auto join(std::initializer_list list, + string_view sep) -> join_view { return join(std::begin(list), std::end(list), sep); } diff --git a/test/ranges-test.cc b/test/ranges-test.cc index e05ae148f55d6..39a16989eee55 100644 --- a/test/ranges-test.cc +++ b/test/ranges-test.cc @@ -268,6 +268,35 @@ 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 + +#ifdef __cpp_lib_ranges +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\""); +} +#endif + // A range that provides non-const only begin()/end() to test fmt::join // handles that. //