From 121002d7002db78c38414cf2d8e8e01e089811ca Mon Sep 17 00:00:00 2001 From: Victor Zverovich Date: Sat, 18 Dec 2021 07:12:53 -0800 Subject: [PATCH] Add a map formatter --- include/fmt/ranges.h | 54 +++++++++++++++++++++++++++++++++++++++++++- test/ranges-test.cc | 2 +- 2 files changed, 54 insertions(+), 2 deletions(-) diff --git a/include/fmt/ranges.h b/include/fmt/ranges.h index ee12bd95..3068649f 100644 --- a/include/fmt/ranges.h +++ b/include/fmt/ranges.h @@ -71,7 +71,7 @@ OutputIterator copy(wchar_t ch, OutputIterator out) { return out; } -/// Return true value if T has std::string interface, like std::string_view. +// Returns true if T has a std::string-like interface, like std::string_view. template class is_std_string_like { template static auto check(U* p) @@ -86,6 +86,19 @@ template class is_std_string_like { template struct is_std_string_like> : std::true_type {}; +template class is_map { + template static auto check(U*) -> typename U::key_type; + template static void check(...); + + public: +#ifdef FMT_FORMAT_MAP_AS_LIST + static FMT_CONSTEXPR_DECL const bool value = false; +#else + static FMT_CONSTEXPR_DECL const bool value = + !std::is_void(nullptr))>::value; +#endif +}; + template struct conditional_helper {}; template struct is_range_ : std::false_type {}; @@ -573,6 +586,7 @@ struct formatter::value>> { template struct is_range { static FMT_CONSTEXPR_DECL const bool value = detail::is_range_::value && !detail::is_std_string_like::value && + !detail::is_map::value && !std::is_convertible>::value && !std::is_constructible, T>::value; }; @@ -614,6 +628,44 @@ struct formatter< } }; +template +struct formatter< + T, Char, + enable_if_t< + detail::is_map::value +// Workaround a bug in MSVC 2019 and earlier. +#if !FMT_MSC_VER + && (is_formattable, Char>::value || + detail::has_fallback_formatter, Char>::value) +#endif + >> { + template + FMT_CONSTEXPR auto parse(ParseContext& ctx) -> decltype(ctx.begin()) { + return ctx.begin(); + } + + template < + typename FormatContext, typename U, + FMT_ENABLE_IF( + std::is_same::value, + const T, T>>::value)> + auto format(U& map, FormatContext& ctx) -> decltype(ctx.out()) { + auto out = ctx.out(); + *out++ = '{'; + int i = 0; + for (const auto& item : map) { + if (i > 0) out = detail::write_delimiter(out); + out = detail::write_range_entry(out, item.first); + *out++ = ':'; + *out++ = ' '; + out = detail::write_range_entry(out, item.second); + ++i; + } + *out++ = '}'; + return out; + } +}; + template struct tuple_join_view : detail::view { const std::tuple& tuple; basic_string_view sep; diff --git a/test/ranges-test.cc b/test/ranges-test.cc index de785c0f..4481e2b4 100644 --- a/test/ranges-test.cc +++ b/test/ranges-test.cc @@ -55,7 +55,7 @@ TEST(ranges_test, format_vector2) { TEST(ranges_test, format_map) { auto m = std::map{{"one", 1}, {"two", 2}}; - EXPECT_EQ(fmt::format("{}", m), "[(\"one\", 1), (\"two\", 2)]"); + EXPECT_EQ(fmt::format("{}", m), "{\"one\": 1, \"two\": 2}"); } TEST(ranges_test, format_pair) {