Cleanup handling of adaptors

This commit is contained in:
Victor Zverovich 2026-01-31 19:35:50 -08:00
parent e55a02b39a
commit 76d480c6fd
2 changed files with 32 additions and 46 deletions

View File

@ -746,18 +746,8 @@ struct formatter<tuple_join_view<Tuple, Char>, Char,
return do_format(value, ctx, std::integral_constant<size_t, N - 1>()); return do_format(value, ctx, std::integral_constant<size_t, N - 1>());
} }
}; };
namespace detail { namespace detail {
// Check if T has an interface like a container adaptor (e.g. std::stack,
// std::queue, std::priority_queue).
template <typename T> class is_container_adaptor_like {
template <typename U> static auto check(U* p) -> typename U::container_type;
template <typename> static void check(...);
public:
static constexpr bool value =
!std::is_void<decltype(check<T>(nullptr))>::value;
};
template <typename Container> struct all { template <typename Container> struct all {
const Container& c; const Container& c;
auto begin() const -> typename Container::const_iterator { return c.begin(); } auto begin() const -> typename Container::const_iterator { return c.begin(); }
@ -766,13 +756,20 @@ template <typename Container> struct all {
} // namespace detail } // namespace detail
/** /**
* returns "true" if "T" is a container adaptor (like "std::stack") * Specifies if `T` is a container adaptor (like `std::stack`) that should be
* that should be formatted by iterating over the underlying container. * formatted as the underlying container.
* */ */
FMT_EXPORT FMT_EXPORT
template <typename T> template <typename T>
struct is_container_adaptor : detail::is_container_adaptor_like<T> {}; struct is_container_adaptor {
private:
template <typename U> static auto check(U* p) -> typename U::container_type;
template <typename> static void check(...);
public:
static constexpr bool value =
!std::is_void<decltype(check<T>(nullptr))>::value;
};
template <typename T, typename Char> template <typename T, typename Char>
struct formatter< struct formatter<

View File

@ -128,7 +128,7 @@ TEST(ranges_test, format_set) {
// Models std::flat_set close enough to test if no ambiguous lookup of a // Models std::flat_set close enough to test if no ambiguous lookup of a
// formatter happens due to the flat_set type matching is_set and // formatter happens due to the flat_set type matching is_set and
// is_container_adaptor_like. // is_container_adaptor.
template <typename T> class flat_set { template <typename T> class flat_set {
public: public:
using key_type = T; using key_type = T;
@ -608,12 +608,11 @@ TEST(ranges_test, vector_char) {
TEST(ranges_test, container_adaptor) { TEST(ranges_test, container_adaptor) {
{ {
using fmt::detail::is_container_adaptor_like;
using T = std::nullptr_t; using T = std::nullptr_t;
static_assert(is_container_adaptor_like<std::stack<T>>::value, ""); static_assert(fmt::is_container_adaptor<std::stack<T>>::value, "");
static_assert(is_container_adaptor_like<std::queue<T>>::value, ""); static_assert(fmt::is_container_adaptor<std::queue<T>>::value, "");
static_assert(is_container_adaptor_like<std::priority_queue<T>>::value, ""); static_assert(fmt::is_container_adaptor<std::priority_queue<T>>::value, "");
static_assert(!is_container_adaptor_like<std::vector<T>>::value, ""); static_assert(!fmt::is_container_adaptor<std::vector<T>>::value, "");
} }
{ {
@ -798,32 +797,22 @@ struct not_range {
}; };
static_assert(!fmt::is_formattable<not_range>{}, ""); static_assert(!fmt::is_formattable<not_range>{}, "");
namespace test_detail { struct test_adaptor {
template <typename T> using container_type = std::vector<int>;
struct partial_opt_out_wrapper { std::vector<int> c = {1, 2, 3};
using container_type = std::vector<T>; };
std::vector<T> c = {1, 2, 3};
};
} // namespace test_detail
namespace fmt { namespace fmt {
template <typename T> template <> struct is_container_adaptor<test_adaptor> : std::false_type {};
struct is_container_adaptor<test_detail::partial_opt_out_wrapper<T>> : std::false_type {};
template <typename T> template <> struct formatter<test_adaptor> : formatter<string_view> {
struct formatter<test_detail::partial_opt_out_wrapper<T>> { auto format(const test_adaptor&, format_context& ctx) const
constexpr fmt::format_parse_context::iterator parse(fmt::format_parse_context& ctx) const { -> format_context::iterator {
return ctx.begin(); return formatter<string_view>::format("test", ctx);
} }
};
fmt::format_context::iterator format(const test_detail::partial_opt_out_wrapper<T>& val,
fmt::format_context& ctx) const {
return fmt::format_to(ctx.out(), "PartialOptOut(size={})", val.c.size());
}
};
} // namespace fmt } // namespace fmt
TEST(ranges_test, container_adaptor_partial_specialization) { TEST(ranges_test, container_adaptor_opt_out) {
test_detail::partial_opt_out_wrapper<int> obj; EXPECT_EQ(fmt::format("{}", test_adaptor()), "test");
EXPECT_EQ(fmt::format("{}", obj), "PartialOptOut(size=3)"); }
}