Fix container adaptor formatting

This commit is contained in:
Victor Zverovich 2023-02-10 09:45:27 -08:00
parent 581c6292c9
commit 655046d24f
2 changed files with 17 additions and 13 deletions

View File

@ -157,8 +157,7 @@ struct has_mutable_begin_end<
decltype(detail::range_end(std::declval<T>())), decltype(detail::range_end(std::declval<T>())),
// the extra int here is because older versions of MSVC don't // the extra int here is because older versions of MSVC don't
// SFINAE properly unless there are distinct types // SFINAE properly unless there are distinct types
int>> int>> : std::true_type {};
: std::true_type {};
template <typename T> template <typename T>
struct is_range_<T, void> struct is_range_<T, void>
@ -663,7 +662,7 @@ struct formatter<tuple_join_view<Char, T...>, Char> {
}; };
namespace detail { namespace detail {
// Check if T has an interface like container adapter (e.g. std::stack, // Check if T has an interface like a container adaptor (e.g. std::stack,
// std::queue, std::priority_queue). // std::queue, std::priority_queue).
template <typename T> class is_container_adaptor_like { template <typename T> class is_container_adaptor_like {
template <typename U> static auto check(U* p) -> typename U::container_type; template <typename U> static auto check(U* p) -> typename U::container_type;
@ -673,20 +672,27 @@ template <typename T> class is_container_adaptor_like {
static constexpr const bool value = static constexpr const bool value =
!std::is_void<decltype(check<T>(nullptr))>::value; !std::is_void<decltype(check<T>(nullptr))>::value;
}; };
template <typename Container> struct all {
const Container& c;
auto begin() const -> typename Container::const_iterator { return c.begin(); }
auto end() const -> typename Container::const_iterator { return c.end(); };
};
} // namespace detail } // namespace detail
template <typename T, typename Char> template <typename T, typename Char>
struct formatter<T, Char, struct formatter<T, Char,
enable_if_t<detail::is_container_adaptor_like<T>::value>> enable_if_t<detail::is_container_adaptor_like<T>::value>>
: formatter<typename T::container_type, Char> { : formatter<detail::all<typename T::container_type>, Char> {
using all = detail::all<typename T::container_type>;
template <typename FormatContext> template <typename FormatContext>
auto format(const T& t, FormatContext& ctx) const -> decltype(ctx.out()) { auto format(const T& t, FormatContext& ctx) const -> decltype(ctx.out()) {
struct getter : T { struct getter : T {
static auto get(const T& t) -> const typename T::container_type& { static auto get(const T& t) -> all {
return t.*(&getter::c); // Access c through the derived class. return {t.*(&getter::c)}; // Access c through the derived class.
} }
}; };
return formatter<typename T::container_type>::format(getter::get(t), ctx); return formatter<all>::format(getter::get(t), ctx);
} }
}; };

View File

@ -12,10 +12,10 @@
#include "fmt/ranges.h" #include "fmt/ranges.h"
#include <map> #include <map>
#include <queue>
#include <stack>
#include <string> #include <string>
#include <vector> #include <vector>
#include <stack>
#include <queue>
#include "gtest/gtest.h" #include "gtest/gtest.h"
@ -447,10 +447,8 @@ TEST(ranges_test, container_adaptor) {
std::stack<char, std::string> s; std::stack<char, std::string> s;
s.push('a'); s.push('a');
s.push('b'); s.push('b');
// Note: The output is formatted as a string because the underlying // See https://cplusplus.github.io/LWG/issue3881.
// container is a string. This behavior is conforming to the standard EXPECT_EQ(fmt::format("{}", s), "['a', 'b']");
// [container.adaptors.format].
EXPECT_EQ(fmt::format("{}", s), "ab");
} }
{ {