diff --git a/include/fmt/ranges.h b/include/fmt/ranges.h index 96b93ea8..3d9bcaf0 100644 --- a/include/fmt/ranges.h +++ b/include/fmt/ranges.h @@ -483,7 +483,7 @@ struct range_formatter< template auto format(R&& range, FormatContext& ctx) const -> decltype(ctx.out()) { - detail::range_mapper> mapper; + auto mapper = detail::range_mapper>(); auto out = ctx.out(); auto it = detail::range_begin(range); auto end = detail::range_end(range); @@ -544,40 +544,39 @@ struct formatter< } }; +// A map formatter. template struct formatter< R, Char, enable_if_t::value == range_format::map>> { private: using map_type = detail::maybe_const_range; - detail::range_formatter_type> - underlying_; + using element_type = detail::uncvref_type; + + decltype(detail::tuple::get_formatters( + detail::tuple_index_sequence())) formatters_; bool no_delimiters_ = false; public: - FMT_CONSTEXPR formatter() { - underlying_.set_brackets({}, {}); - underlying_.set_separator(detail::string_literal{}); - } + FMT_CONSTEXPR formatter() {} template FMT_CONSTEXPR auto parse(ParseContext& ctx) -> decltype(ctx.begin()) { auto it = ctx.begin(); auto end = ctx.end(); - detail::maybe_set_debug_format(underlying_, true); - if (it == end) return underlying_.parse(ctx); - - if (detail::to_ascii(*it) == 'n') { - no_delimiters_ = true; - ++it; + if (it != end) { + if (detail::to_ascii(*it) == 'n') { + no_delimiters_ = true; + ++it; + } + if (it != end && *it != '}') { + if (*it != ':') report_error("invalid format specifier"); + ++it; + } + ctx.advance_to(it); } - if (it != end && *it != '}') { - if (*it != ':') report_error("invalid format specifier"); - detail::maybe_set_debug_format(underlying_, false); - ++it; - } - ctx.advance_to(it); - return underlying_.parse(ctx); + detail::for_each(formatters_, detail::parse_empty_specs{ctx}); + return it; } template @@ -594,7 +593,9 @@ struct formatter< for (; it != end; ++it) { if (i > 0) out = detail::copy(sep, out); ctx.advance_to(out); - out = underlying_.format(mapper.map(*it), ctx); + detail::for_each2(formatters_, mapper.map(*it), + detail::format_tuple_element{ + 0, ctx, detail::string_literal{}}); ++i; } basic_string_view close = detail::string_literal{}; diff --git a/test/ranges-test.cc b/test/ranges-test.cc index 7905fd49..466e5848 100644 --- a/test/ranges-test.cc +++ b/test/ranges-test.cc @@ -89,6 +89,35 @@ TEST(ranges_test, format_map) { EXPECT_EQ(fmt::format("{:n}", m), "\"one\": 1, \"two\": 2"); } +struct test_map_value {}; + +FMT_BEGIN_NAMESPACE +template <> struct formatter : formatter { + auto format(test_map_value, format_context& ctx) const + -> format_context::iterator { + return formatter::format("foo", ctx); + } +}; + +template +struct formatter> : formatter { + auto format(std::pair, format_context& ctx) const + -> format_context::iterator { + return ctx.out(); + } +}; + +template +struct is_tuple_formattable, char> + : std::false_type {}; + +FMT_END_NAMESPACE + +TEST(ranges_test, format_map_custom_pair) { + EXPECT_EQ(fmt::format("{}", std::map{{42, {}}}), + "{42: \"foo\"}"); +} + TEST(ranges_test, format_set) { EXPECT_EQ(fmt::format("{}", std::set{"one", "two"}), "{\"one\", \"two\"}"); @@ -468,7 +497,7 @@ struct vec { auto begin(const vec& v) -> const int* { return v.n; } auto end(const vec& v) -> const int* { return v.n + 2; } -} +} // namespace adl TEST(ranges_test, format_join_adl_begin_end) { EXPECT_EQ(fmt::format("{}", fmt::join(adl::vec(), "/")), "42/43");