From 5a3576acc88cb0947cb68f7f46c320aae6f44090 Mon Sep 17 00:00:00 2001 From: Vladislav Shchapov Date: Sat, 9 Nov 2024 21:28:46 +0500 Subject: [PATCH] Implement fmt::join for tuple-like objects (#4230) Signed-off-by: Vladislav Shchapov --- include/fmt/ranges.h | 41 ++++++++++++++++++++++------------------- include/fmt/xchar.h | 8 ++++---- test/ranges-test.cc | 9 ++++++++- 3 files changed, 34 insertions(+), 24 deletions(-) diff --git a/include/fmt/ranges.h b/include/fmt/ranges.h index c6020abe..b4076c43 100644 --- a/include/fmt/ranges.h +++ b/include/fmt/ranges.h @@ -696,18 +696,18 @@ auto join(It begin, Sentinel end, string_view sep) -> join_view { * fmt::print("{:02}", fmt::join(v, ", ")); * // Output: 01, 02, 03 */ -template +template ::value)> auto join(Range&& r, string_view sep) -> join_view { return {detail::range_begin(r), detail::range_end(r), sep}; } -template struct tuple_join_view : detail::view { - const std::tuple& tuple; +template struct tuple_join_view : detail::view { + const Tuple& tuple; basic_string_view sep; - tuple_join_view(const std::tuple& t, basic_string_view s) + tuple_join_view(const Tuple& t, basic_string_view s) : tuple(t), sep{s} {} }; @@ -718,21 +718,22 @@ template struct tuple_join_view : detail::view { # define FMT_TUPLE_JOIN_SPECIFIERS 0 #endif -template -struct formatter, Char> { +template +struct formatter, Char, + enable_if_t::value>> { FMT_CONSTEXPR auto parse(parse_context& ctx) -> const Char* { - return do_parse(ctx, std::integral_constant()); + return do_parse(ctx, std::tuple_size()); } template - auto format(const tuple_join_view& value, + auto format(const tuple_join_view& value, FormatContext& ctx) const -> typename FormatContext::iterator { - return do_format(value, ctx, - std::integral_constant()); + return do_format(value, ctx, std::tuple_size()); } private: - std::tuple::type, Char>...> formatters_; + decltype(detail::tuple::get_formatters( + detail::tuple_index_sequence())) formatters_; FMT_CONSTEXPR auto do_parse(parse_context& ctx, std::integral_constant) @@ -746,7 +747,7 @@ struct formatter, Char> { -> const Char* { auto end = ctx.begin(); #if FMT_TUPLE_JOIN_SPECIFIERS - end = std::get(formatters_).parse(ctx); + end = std::get::value - N>(formatters_).parse(ctx); if (N > 1) { auto end1 = do_parse(ctx, std::integral_constant()); if (end != end1) @@ -757,18 +758,20 @@ struct formatter, Char> { } template - auto do_format(const tuple_join_view&, FormatContext& ctx, + auto do_format(const tuple_join_view&, FormatContext& ctx, std::integral_constant) const -> typename FormatContext::iterator { return ctx.out(); } template - auto do_format(const tuple_join_view& value, FormatContext& ctx, + auto do_format(const tuple_join_view& value, FormatContext& ctx, std::integral_constant) const -> typename FormatContext::iterator { - auto out = std::get(formatters_) - .format(std::get(value.tuple), ctx); + using std::get; + auto out = + std::get::value - N>(formatters_) + .format(get::value - N>(value.tuple), ctx); if (N <= 1) return out; out = detail::copy(value.sep, out); ctx.advance_to(out); @@ -825,9 +828,9 @@ FMT_BEGIN_EXPORT * fmt::print("{}", fmt::join(t, ", ")); * // Output: 1, a */ -template -FMT_CONSTEXPR auto join(const std::tuple& tuple, string_view sep) - -> tuple_join_view { +template ::value)> +FMT_CONSTEXPR auto join(const Tuple& tuple, string_view sep) + -> tuple_join_view { return {tuple, sep}; } diff --git a/include/fmt/xchar.h b/include/fmt/xchar.h index 08f11231..4cbda542 100644 --- a/include/fmt/xchar.h +++ b/include/fmt/xchar.h @@ -140,7 +140,7 @@ auto join(It begin, Sentinel end, wstring_view sep) return {begin, end, sep}; } -template +template ::value)> auto join(Range&& range, wstring_view sep) -> join_view { @@ -153,9 +153,9 @@ auto join(std::initializer_list list, wstring_view sep) return join(std::begin(list), std::end(list), sep); } -template -auto join(const std::tuple& tuple, basic_string_view sep) - -> tuple_join_view { +template ::value)> +auto join(const Tuple& tuple, basic_string_view sep) + -> tuple_join_view { return {tuple, sep}; } diff --git a/test/ranges-test.cc b/test/ranges-test.cc index a3b659db..bff35f3d 100644 --- a/test/ranges-test.cc +++ b/test/ranges-test.cc @@ -213,7 +213,8 @@ TEST(ranges_test, tuple_parse_calls_element_parse) { EXPECT_THROW(f.parse(ctx), bad_format); } -#ifdef FMT_RANGES_TEST_ENABLE_FORMAT_STRUCT +#if defined(FMT_RANGES_TEST_ENABLE_JOIN) || \ + defined(FMT_RANGES_TEST_ENABLE_FORMAT_STRUCT) struct tuple_like { int i; std::string str; @@ -241,7 +242,9 @@ template struct tuple_element { using type = decltype(std::declval().get()); }; } // namespace std +#endif +#ifdef FMT_RANGES_TEST_ENABLE_FORMAT_STRUCT TEST(ranges_test, format_struct) { auto t = tuple_like{42, "foo"}; EXPECT_EQ(fmt::format("{}", t), "(42, \"foo\")"); @@ -420,6 +423,10 @@ TEST(ranges_test, join_tuple) { auto t4 = std::tuple(4.0f); EXPECT_EQ(fmt::format("{}", fmt::join(t4, "/")), "4"); + // Tuple-like. + auto t5 = tuple_like{42, "foo"}; + EXPECT_EQ(fmt::format("{}", fmt::join(t5, ", ")), "42, foo"); + # if FMT_TUPLE_JOIN_SPECIFIERS // Specs applied to each element. auto t5 = std::tuple(-3, 100, 1);