From bacf741a92a9a84becd23542b73186da4e4acbf6 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Tomasz=20Kami=C5=84ski?= Date: Fri, 18 Apr 2025 14:56:39 +0200 Subject: libstdc++: Implement formatters for queue, priority_queue and stack [PR109162] MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit This patch implements formatter specializations for standard container adaptors (queue, priority_queue and stack) from P2286R8. To be able to access the protected `c` member, the adaptors befriend corresponding formatter specializations. Note that such specialization may be disable if the container is formattable, in such case specializations are unharmful. As in the case of previous commits, the signatures of the user-facing parse and format methods of the provided formatters deviate from the standard by constraining types of parameters: * _CharT is constrained __formatter::__char * basic_format_parse_context<_CharT> for parse argument * basic_format_context<_Out, _CharT> for format second argument The standard specifies all above as unconstrained types. In particular _CharT constrain, allow us to befriend all allowed specializations. Furthermore the standard specifies these formatters as delegating to formatter, charT>, which in turn delegates to range_formatter. This patch avoids one level of indirection, and dependency of ranges::ref_view. This is technically observable if user specializes formatter> where PD is program defined container, but I do not think this is the case worth extra indirection. This patch also moves the formattable and it's dependencies to the formatfwd.h, so it can be used in adapters formatters, without including format header. The definition of _Iter_for is changed from alias to denoting back_insert_iterator>, to struct with type nested typedef that points to same type, that is forward declared. PR libstdc++/109162 libstdc++-v3/ChangeLog: * include/bits/formatfwd.h (__format::__parsable_with) (__format::__formattable_with, __format::__formattable_impl) (__format::__has_debug_format, __format::__const_formattable_range) (__format::__maybe_const_range, __format::__maybe_const) (std::formattable): Moved from std/format. (__format::Iter_for, std::range_formatter): Forward declare. * include/bits/stl_queue.h (std::formatter): Forward declare. (std::queue, std::priority_queue): Befriend formatter specializations. * include/bits/stl_stack.h (std::formatter): Forward declare. (std::stack): Befriend formatter specializations. * include/std/format (__format::_Iter_for): Define as struct with (__format::__parsable_with, __format::__formattable_with) (__format::__formattable_impl, __format::__has_debug_format) (_format::__const_formattable_range, __format::__maybe_const_range) (__format::__maybe_const, std::formattable): Moved to bits/formatfwd.h. (std::range_formatter): Remove default argument specified in declaration in bits/formatfwd.h. * include/std/queue: Include bits/version.h before bits/stl_queue.h. (formatter, _CharT>) (formatter, _CharT>): Define. * include/std/stack: Include bits/version.h before bits/stl_stack.h (formatter, _CharT>): Define. * testsuite/std/format/ranges/adaptors.cc: New test. Reviewed-by: Jonathan Wakely Signed-off-by: Tomasz KamiƄski --- .../testsuite/std/format/ranges/adaptors.cc | 156 +++++++++++++++++++++ 1 file changed, 156 insertions(+) create mode 100644 libstdc++-v3/testsuite/std/format/ranges/adaptors.cc (limited to 'libstdc++-v3/testsuite/std') diff --git a/libstdc++-v3/testsuite/std/format/ranges/adaptors.cc b/libstdc++-v3/testsuite/std/format/ranges/adaptors.cc new file mode 100644 index 0000000..854c7ee --- /dev/null +++ b/libstdc++-v3/testsuite/std/format/ranges/adaptors.cc @@ -0,0 +1,156 @@ +// { dg-do run { target c++23 } } +// { dg-timeout-factor 2 } + +#include +#include +#include +#include + +template +bool +is_format_string_for(const char* str, Args&&... args) +{ + try { + (void) std::vformat(str, std::make_format_args(args...)); + return true; + } catch (const std::format_error&) { + return false; + } +} + +#define WIDEN_(C, S) ::std::__format::_Widen(S, L##S) +#define WIDEN(S) WIDEN_(_CharT, S) + +template class Adaptor> +void +test_format_string() +{ + Adaptor q; + VERIFY( !is_format_string_for("{:?}", q) ); + VERIFY( !is_format_string_for("{:P}", q) ); + + // width needs to be integer type + VERIFY( !is_format_string_for("{:{}}", q, 1.0f) ); +} + +struct NoFormat +{ + friend auto operator<=>(NoFormat, NoFormat) = default; +}; + +struct MutFormat +{ + MutFormat() = default; + MutFormat(int p) : x(p) {} + + int x; + friend auto operator<=>(MutFormat, MutFormat) = default; +}; + +template +struct std::formatter + : std::formatter +{ + template + Out format(MutFormat& mf, basic_format_context& ctx) const + { return std::formatter::format(mf.x, ctx); } +}; + +template +struct NotFormattableCont : std::vector +{ + using std::vector::vector; +}; + +template +constexpr auto std::format_kind> + = std::range_format::disabled; + +template> class Adaptor> +void +test_output() +{ + const std::vector v{3, 2, 1}; + std::basic_string<_CharT> res; + Adaptor> q(std::from_range, v); + + res = std::format(WIDEN("{}"), q); + VERIFY( res == WIDEN("[3, 2, 1]") ); + + res = std::format(WIDEN("{}"), std::as_const(q)); + VERIFY( res == WIDEN("[3, 2, 1]") ); + + res = std::format(WIDEN("{:n:#x}"), q); + VERIFY( res == WIDEN("0x3, 0x2, 0x1") ); + + res = std::format(WIDEN("{:=^23:#04x}"), q); + VERIFY( res == WIDEN("==[0x03, 0x02, 0x01]===") ); + + // Sequence output is always used + std::queue<_CharT, std::basic_string<_CharT>> qs( + std::from_range, + std::basic_string_view<_CharT>(WIDEN("321"))); + + res = std::format(WIDEN("{}"), qs); + VERIFY( res == WIDEN("['3', '2', '1']") ); + + res = std::format(WIDEN("{::}"), std::as_const(qs)); + VERIFY( res == WIDEN("[3, 2, 1]") ); + + res = std::format(WIDEN("{:?s}"), qs); + VERIFY( res == WIDEN(R"("321")") ); + + Adaptor> qd(std::from_range, v); + + res = std::format(WIDEN("{}"), qd); + VERIFY( res == WIDEN("[3, 2, 1]") ); + + res = std::format(WIDEN("{}"), std::as_const(qd)); + VERIFY( res == WIDEN("[3, 2, 1]") ); + + Adaptor mq(std::from_range, v); + + res = std::format(WIDEN("{}"), mq); + VERIFY( res == WIDEN("[3, 2, 1]") ); + + static_assert(!std::formattable, _CharT>); + + static_assert(!std::formattable, _CharT>); + static_assert(!std::formattable, _CharT>); + + // Formatter check if container is formattable, not container elements. + static_assert(!std::formattable>, _CharT>); +} + +template> class Adaptor> +void +test_adaptor() +{ + test_format_string(); + test_output(); + test_output(); + + static_assert(!std::formattable, int>); + static_assert(!std::formattable, char32_t>); +} + +template +void +test_compare() +{ + const std::vector v{3, 2, 1}; + std::basic_string<_CharT> res; + std::priority_queue, std::greater<>> q( + std::from_range, v); + + res = std::format(WIDEN("{}"), q); + VERIFY( res == WIDEN("[1, 2, 3]") ); +} + +int main() +{ + test_adaptor(); + test_adaptor(); + test_compare(); +} -- cgit v1.1