Fix #4123: Add fmt::is_container_adaptor trait to allow opt-out of adaptor formatting

This commit is contained in:
Soumik15630m 2026-01-22 12:27:07 +05:30
parent a9ea225cad
commit 5487689a57
2 changed files with 58 additions and 0 deletions

View File

@ -347,6 +347,16 @@ template <typename T, typename Char> struct is_range {
detail::is_range_<T>::value && !detail::has_to_string_view<T>::value;
};
/**
* returns "true" if "T" is a container adaptor (like "std::stack")
* that should be formatted by iterating over the underlying container.
* */
FMT_EXPORT
template <typename T>
struct is_container_adaptor : std::true_type {};
namespace detail {
template <typename Char, typename Element>
@ -770,6 +780,7 @@ template <typename T, typename Char>
struct formatter<
T, Char,
enable_if_t<conjunction<detail::is_container_adaptor_like<T>,
is_container_adaptor<T>, // -> added the sanity checker
bool_constant<range_format_kind<T, Char>::value ==
range_format::disabled>>::value>>
: formatter<detail::all<typename T::container_type>, Char> {

View File

@ -5,6 +5,10 @@
//
// For the license information refer to format.h.
#include "fmt/ranges.h"
#include "fmt/ranges.h"
#include "fmt/ranges.h"
#include "fmt/ranges.h"
#include "fmt/ranges.h"
#include <array>
@ -17,6 +21,9 @@
#include <utility>
#include <vector>
#include "posix-mock.h"
#include "fmt/base.h"
#if FMT_CPLUSPLUS > 201703L && FMT_HAS_INCLUDE(<ranges>)
# include <ranges>
#endif
@ -797,3 +804,43 @@ struct not_range {
void end() const {}
};
static_assert(!fmt::is_formattable<not_range>{}, "");
namespace test_detail
{
template <typename T>
struct partial_opt_out_wrapper
{
using container_type = std::vector<T>;
std::vector<T> c = {1, 2, 3};
auto begin() const {return c.begin(); }
auto end() const {return c.end(); }
};
}
namespace fmt
{
// disables is_range
template <typename T>
struct fmt::is_range<test_detail::partial_opt_out_wrapper<T>, char> : std::false_type{};
// opt-out of the container adaptor format
template <typename T>
struct fmt::is_container_adaptor<test_detail::partial_opt_out_wrapper<T>> : std::false_type{};
}
//a custom partial specializtion
template <typename T>
struct fmt::formatter<test_detail::partial_opt_out_wrapper<T>>
{
constexpr auto parse(fmt::format_parse_context& ctx) { return ctx.begin(); }
auto 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());
}
};
TEST(ranges_test, container_adaptor_partial_specialization)
{
test_detail::partial_opt_out_wrapper<int> obj;
EXPECT_EQ(fmt::format("{}", obj), "PartialOptOut(size=3)");
}