From 581c6292c98f998622f6adf0e6f0f5cb47704269 Mon Sep 17 00:00:00 2001 From: Shawn Zhong Date: Wed, 8 Feb 2023 19:25:41 -0600 Subject: [PATCH] Add formatters for container adapters (#3279) --- include/fmt/ranges.h | 28 ++++++++++++++++++++ test/ranges-test.cc | 63 ++++++++++++++++++++++++++++++++++++++++++++ 2 files changed, 91 insertions(+) diff --git a/include/fmt/ranges.h b/include/fmt/ranges.h index 3ea41c8c..3377efca 100644 --- a/include/fmt/ranges.h +++ b/include/fmt/ranges.h @@ -662,6 +662,34 @@ struct formatter, Char> { } }; +namespace detail { +// Check if T has an interface like container adapter (e.g. std::stack, +// std::queue, std::priority_queue). +template class is_container_adaptor_like { + template static auto check(U* p) -> typename U::container_type; + template static void check(...); + + public: + static constexpr const bool value = + !std::is_void(nullptr))>::value; +}; +} // namespace detail + +template +struct formatter::value>> + : formatter { + template + auto format(const T& t, FormatContext& ctx) const -> decltype(ctx.out()) { + struct getter : T { + static auto get(const T& t) -> const typename T::container_type& { + return t.*(&getter::c); // Access c through the derived class. + } + }; + return formatter::format(getter::get(t), ctx); + } +}; + FMT_MODULE_EXPORT_BEGIN /** diff --git a/test/ranges-test.cc b/test/ranges-test.cc index 8cafeec4..16fd0734 100644 --- a/test/ranges-test.cc +++ b/test/ranges-test.cc @@ -14,6 +14,8 @@ #include #include #include +#include +#include #include "gtest/gtest.h" @@ -406,3 +408,64 @@ TEST(ranges_test, range_of_range_of_mixed_const) { TEST(ranges_test, vector_char) { EXPECT_EQ(fmt::format("{}", std::vector{'a', 'b'}), "['a', 'b']"); } + +TEST(ranges_test, container_adaptor) { + { + using fmt::detail::is_container_adaptor_like; + using T = std::nullptr_t; + static_assert(is_container_adaptor_like>::value, ""); + static_assert(is_container_adaptor_like>::value, ""); + static_assert(is_container_adaptor_like>::value, ""); + static_assert(!is_container_adaptor_like>::value, ""); + } + + { + std::stack s; + s.push(1); + s.push(2); + EXPECT_EQ(fmt::format("{}", s), "[1, 2]"); + EXPECT_EQ(fmt::format("{}", const_cast(s)), "[1, 2]"); + } + + { + std::queue q; + q.push(1); + q.push(2); + EXPECT_EQ(fmt::format("{}", q), "[1, 2]"); + } + + { + std::priority_queue q; + q.push(3); + q.push(1); + q.push(2); + q.push(4); + EXPECT_EQ(fmt::format("{}", q), "[4, 3, 2, 1]"); + } + + { + std::stack s; + s.push('a'); + s.push('b'); + // Note: The output is formatted as a string because the underlying + // container is a string. This behavior is conforming to the standard + // [container.adaptors.format]. + EXPECT_EQ(fmt::format("{}", s), "ab"); + } + + { + struct my_container_adaptor { + using value_type = int; + using container_type = std::vector; + void push(const value_type& v) { c.push_back(v); } + + protected: + container_type c; + }; + + my_container_adaptor m; + m.push(1); + m.push(2); + EXPECT_EQ(fmt::format("{}", m), "[1, 2]"); + } +}