From a44c8f651bd97192f480346b1d084b0c22f7391a Mon Sep 17 00:00:00 2001 From: sunmy2019 <59365878+sunmy2019@users.noreply.github.com> Date: Tue, 24 Aug 2021 00:51:19 +0800 Subject: [PATCH] reimplement `formatter` (#2457) * reimplement `formatter` 1. completely reimplement `formatter, Char>` 2. Add some tests * use FMT_THROW --- include/fmt/ranges.h | 54 ++++++++++++++++++++++++++++---------------- test/ranges-test.cc | 13 +++++++++++ 2 files changed, 47 insertions(+), 20 deletions(-) diff --git a/include/fmt/ranges.h b/include/fmt/ranges.h index a35c0664..2eb5ffe2 100644 --- a/include/fmt/ranges.h +++ b/include/fmt/ranges.h @@ -629,42 +629,56 @@ template struct formatter, Char> { template FMT_CONSTEXPR auto parse(ParseContext& ctx) -> decltype(ctx.begin()) { - return ctx.begin(); + return do_parse(ctx, std::integral_constant{}); } template - auto format(const tuple_join_view& value, FormatContext& ctx) -> - typename FormatContext::iterator { - return format(value, ctx, detail::make_index_sequence{}); + auto format(const tuple_join_view& value, + FormatContext& ctx) const -> typename FormatContext::iterator { + return do_format(value, ctx, + std::integral_constant{}); } private: - template - auto format(const tuple_join_view& value, FormatContext& ctx, - detail::index_sequence) -> - typename FormatContext::iterator { - using std::get; - return format_args(value, ctx, get(value.tuple)...); + std::tuple::type, Char>...> formatters_; + + template + FMT_CONSTEXPR auto do_parse(ParseContext& ctx, + std::integral_constant) + -> decltype(ctx.begin()) { + return ctx.begin(); + } + + template + FMT_CONSTEXPR auto do_parse(ParseContext& ctx, + std::integral_constant) + -> decltype(ctx.begin()) { + auto end = std::get(formatters_).parse(ctx); + if (N > 1) { + auto end1 = do_parse(ctx, std::integral_constant{}); + if (end != end1) + FMT_THROW(format_error("incompatible format specs for tuple elements")); + } + return end; } template - auto format_args(const tuple_join_view&, FormatContext& ctx) -> + auto do_format(const tuple_join_view&, FormatContext& ctx, + std::integral_constant) const -> typename FormatContext::iterator { - // NOTE: for compilers that support C++17, this empty function instantiation - // can be replaced with a constexpr branch in the variadic overload. return ctx.out(); } - template - auto format_args(const tuple_join_view& value, FormatContext& ctx, - const Arg& arg, const Args&... args) -> + template + auto do_format(const tuple_join_view& value, FormatContext& ctx, + std::integral_constant) const -> typename FormatContext::iterator { - using base = formatter::type, Char>; - auto out = base().format(arg, ctx); - if (sizeof...(Args) > 0) { + auto out = std::get(formatters_) + .format(std::get(value.tuple), ctx); + if (N > 1) { out = std::copy(value.sep.begin(), value.sep.end(), out); ctx.advance_to(out); - return format_args(value, ctx, args...); + return do_format(value, ctx, std::integral_constant{}); } return out; } diff --git a/test/ranges-test.cc b/test/ranges-test.cc index 603d1976..7808cdbd 100644 --- a/test/ranges-test.cc +++ b/test/ranges-test.cc @@ -217,6 +217,19 @@ TEST(ranges_test, join_tuple) { // Single element tuple. auto t4 = std::tuple(4.0f); EXPECT_EQ(fmt::format("{}", fmt::join(t4, "/")), "4"); + + // Specs applied to each element. + auto t5 = std::tuple(-3, 100, 1); + EXPECT_EQ(fmt::format("{:+03}", fmt::join(t5, ", ")), "-03, +100, +01"); + + auto t6 = std::tuple(3, 3.14, 3.1415); + EXPECT_EQ(fmt::format("{:5.5f}", fmt::join(t6, ", ")), + "3.00000, 3.14000, 3.14150"); + + // Testing lvalue tuple args. + int y = -1; + auto t7 = std::tuple(3, y, y); + EXPECT_EQ(fmt::format("{:03}", fmt::join(t7, ", ")), "003, -01, -01"); } TEST(ranges_test, join_initializer_list) {