diff --git a/libstdc++-v3/include/bits/formatfwd.h b/libstdc++-v3/include/bits/formatfwd.h index a6b5ac8c8ce1..9ba658b078a5 100644 --- a/libstdc++-v3/include/bits/formatfwd.h +++ b/libstdc++-v3/include/bits/formatfwd.h @@ -37,6 +37,12 @@ // must have been included before this header: #ifdef __glibcxx_format // C++ >= 20 && HOSTED +#include +#include +#if __glibcxx_format_ranges // C++ >= 23 && HOSTED +# include // input_range, range_reference_t +#endif + namespace std _GLIBCXX_VISIBILITY(default) { _GLIBCXX_BEGIN_NAMESPACE_VERSION @@ -50,6 +56,7 @@ _GLIBCXX_BEGIN_NAMESPACE_VERSION // [format.formatter], formatter template struct formatter; +/// @cond undocumented namespace __format { #ifdef _GLIBCXX_USE_WCHAR_T @@ -60,9 +67,80 @@ namespace __format concept __char = same_as<_CharT, char>; #endif + template>, + typename _ParseContext + = basic_format_parse_context> + concept __parsable_with + = semiregular<_Formatter> + && requires (_Formatter __f, _ParseContext __pc) + { + { __f.parse(__pc) } -> same_as; + }; + + template>, + typename _ParseContext + = basic_format_parse_context> + concept __formattable_with + = semiregular<_Formatter> + && requires (const _Formatter __cf, _Tp&& __t, _Context __fc) + { + { __cf.format(__t, __fc) } -> same_as; + }; + + // An unspecified output iterator type used in the `formattable` concept. + template + struct _Iter_for; + template + using _Iter_for_t = typename _Iter_for<_CharT>::type; + + template, _CharT>> + concept __formattable_impl + = __parsable_with<_Tp, _Context> && __formattable_with<_Tp, _Context>; + + template + concept __has_debug_format = requires(_Formatter __f) + { + __f.set_debug_format(); + }; + template<__char _CharT> struct __formatter_int; +} // namespace __format +/// @endcond + +#if __glibcxx_format_ranges // C++ >= 23 && HOSTED + // [format.formattable], concept formattable + template + concept formattable + = __format::__formattable_impl, _CharT>; + + template + requires same_as, _Tp> && formattable<_Tp, _CharT> + class range_formatter; + +/// @cond undocumented +namespace __format +{ + template + concept __const_formattable_range + = ranges::input_range + && formattable, _CharT>; + + template + using __maybe_const_range + = __conditional_t<__const_formattable_range<_Rg, _CharT>, const _Rg, _Rg>; + + template + using __maybe_const + = __conditional_t, const _Tp, _Tp>; } +#endif // format_ranges + _GLIBCXX_END_NAMESPACE_VERSION } // namespace std diff --git a/libstdc++-v3/include/bits/stl_queue.h b/libstdc++-v3/include/bits/stl_queue.h index 554e076aae90..a3a8bc1f0ada 100644 --- a/libstdc++-v3/include/bits/stl_queue.h +++ b/libstdc++-v3/include/bits/stl_queue.h @@ -70,6 +70,10 @@ namespace std _GLIBCXX_VISIBILITY(default) { _GLIBCXX_BEGIN_NAMESPACE_VERSION +#if __glibcxx_format_ranges + template class formatter; +#endif + /** * @brief A standard container giving FIFO behavior. * @@ -369,6 +373,11 @@ _GLIBCXX_BEGIN_NAMESPACE_VERSION swap(c, __q.c); } #endif // __cplusplus >= 201103L + +#if __glibcxx_format_ranges + friend class formatter, char>; + friend class formatter, wchar_t>; +#endif }; #if __cpp_deduction_guides >= 201606 @@ -898,6 +907,11 @@ _GLIBCXX_BEGIN_NAMESPACE_VERSION swap(comp, __pq.comp); } #endif // __cplusplus >= 201103L + +#if __glibcxx_format_ranges + friend class formatter, char>; + friend class formatter, wchar_t>; +#endif }; #if __cpp_deduction_guides >= 201606 diff --git a/libstdc++-v3/include/bits/stl_stack.h b/libstdc++-v3/include/bits/stl_stack.h index 7b324642b322..27c79d6ce588 100644 --- a/libstdc++-v3/include/bits/stl_stack.h +++ b/libstdc++-v3/include/bits/stl_stack.h @@ -70,6 +70,10 @@ namespace std _GLIBCXX_VISIBILITY(default) { _GLIBCXX_BEGIN_NAMESPACE_VERSION +#if __glibcxx_format_ranges + template class formatter; +#endif + /** * @brief A standard container giving FILO behavior. * @@ -343,6 +347,11 @@ _GLIBCXX_BEGIN_NAMESPACE_VERSION swap(c, __s.c); } #endif // __cplusplus >= 201103L + +#if __glibcxx_format_ranges + friend class formatter, char>; + friend class formatter, wchar_t>; +#endif }; #if __cpp_deduction_guides >= 201606 diff --git a/libstdc++-v3/include/std/format b/libstdc++-v3/include/std/format index e557e104d74d..7d3067098bef 100644 --- a/libstdc++-v3/include/std/format +++ b/libstdc++-v3/include/std/format @@ -117,6 +117,11 @@ namespace __format template class _Sink_iter; + // An unspecified output iterator type used in the `formattable` concept. + template + struct _Iter_for + { using type = back_insert_iterator>; }; + template using __format_context = basic_format_context<_Sink_iter<_CharT>, _CharT>; @@ -135,6 +140,7 @@ namespace __format template friend struct std::basic_format_string; }; + } // namespace __format /// @endcond @@ -3024,59 +3030,6 @@ namespace __format : private formatter<__format::__disabled, wchar_t> { }; #endif -/// @cond undocumented -namespace __format -{ - template>, - typename _ParseContext - = basic_format_parse_context> - concept __parsable_with - = semiregular<_Formatter> - && requires (_Formatter __f, _ParseContext __pc) - { - { __f.parse(__pc) } -> same_as; - }; - - template>, - typename _ParseContext - = basic_format_parse_context> - concept __formattable_with - = semiregular<_Formatter> - && requires (const _Formatter __cf, _Tp&& __t, _Context __fc) - { - { __cf.format(__t, __fc) } -> same_as; - }; - - // An unspecified output iterator type used in the `formattable` concept. - template - using _Iter_for = back_insert_iterator>; - - template, _CharT>> - concept __formattable_impl - = __parsable_with<_Tp, _Context> && __formattable_with<_Tp, _Context>; - - template - concept __has_debug_format = requires(_Formatter __f) - { - __f.set_debug_format(); - }; - -} // namespace __format -/// @endcond - -#if __glibcxx_format_ranges // C++ >= 23 && HOSTED - // [format.formattable], concept formattable - template - concept formattable - = __format::__formattable_impl, _CharT>; - -#endif // format_ranges - /// An iterator after the last character written, and the number of /// characters that would have been written. template @@ -5250,26 +5203,13 @@ namespace __format return __format::__write_padded_as_spec(__str, __width, __fc, __spec); } - template - concept __const_formattable_range - = ranges::input_range - && formattable, _CharT>; - // _Rg& and const _Rg& are both formattable and use same formatter // specialization for their references. template concept __simply_formattable_range = __const_formattable_range<_Rg, _CharT> && same_as>, - remove_cvref_t>>; - - template - using __maybe_const_range - = __conditional_t<__const_formattable_range<_Rg, _CharT>, const _Rg, _Rg>; - - template - using __maybe_const - = __conditional_t, const _Tp, _Tp>; + remove_cvref_t>>; template struct __indexed_formatter_storage @@ -5493,7 +5433,7 @@ namespace __format }; // [format.range.formatter], class template range_formatter - template + template requires same_as, _Tp> && formattable<_Tp, _CharT> class range_formatter { diff --git a/libstdc++-v3/include/std/queue b/libstdc++-v3/include/std/queue index 74b6c07b49f1..90525897da75 100644 --- a/libstdc++-v3/include/std/queue +++ b/libstdc++-v3/include/std/queue @@ -61,14 +61,88 @@ #include // containers +#define __glibcxx_want_adaptor_iterator_pair_constructor +#define __glibcxx_want_containers_ranges +#include + #include #include #include #include #include -#define __glibcxx_want_adaptor_iterator_pair_constructor -#define __glibcxx_want_containers_ranges -#include +#ifdef __glibcxx_format_ranges // C++ >= 23 && HOSTED +#include + +namespace std _GLIBCXX_VISIBILITY(default) +{ +_GLIBCXX_BEGIN_NAMESPACE_VERSION + // Standard does not constrain accepted _CharT, we do so we can + // befriend specializations. + template<__format::__char _CharT, typename _Tp, + formattable<_CharT> _Container> + struct formatter, _CharT> + { + private: + using __maybe_const_adaptor + = __conditional_t< + __format::__const_formattable_range<_Container, _CharT>, + const queue<_Tp, _Container>, queue<_Tp, _Container>>; + + public: + // Standard declares this as template accepting unconstrained + // ParseContext type. + constexpr typename basic_format_parse_context<_CharT>::iterator + parse(basic_format_parse_context<_CharT>& __pc) + { return _M_f.parse(__pc); } + + // Standard declares this as template accepting unconstrained + // FormatContext type. + template + typename basic_format_context<_Out, _CharT>::iterator + format(__maybe_const_adaptor& __a, + basic_format_context<_Out, _CharT>& __fc) const + { return _M_f.format(__a.c, __fc); } + + private: + // Standard uses formatter, _CharT>. + range_formatter<_Tp, _CharT> _M_f; + }; + + template<__format::__char _CharT, typename _Tp, + formattable<_CharT> _Container, typename _Compare> + struct formatter, _CharT> + { + private: + using __maybe_const_adaptor + = __conditional_t< + __format::__const_formattable_range<_Container, _CharT>, + const priority_queue<_Tp, _Container, _Compare>, + priority_queue<_Tp, _Container, _Compare>>; + + public: + // Standard declares this as template accepting unconstrained + // ParseContext type. + constexpr typename basic_format_parse_context<_CharT>::iterator + parse(basic_format_parse_context<_CharT>& __pc) + { return _M_f.parse(__pc); } + + // Standard declares this as template accepting unconstrained + // FormatContext type. + template + typename basic_format_context<_Out, _CharT>::iterator + format(__maybe_const_adaptor& __a, + basic_format_context<_Out, _CharT>& __fc) const + { return _M_f.format(__a.c, __fc); } + + private: + // Standard uses formatter, _CharT>. + range_formatter<_Tp, _CharT> _M_f; + }; + +_GLIBCXX_END_NAMESPACE_VERSION +} // namespace std +#endif // __glibcxx_format_ranges + #endif /* _GLIBCXX_QUEUE */ diff --git a/libstdc++-v3/include/std/stack b/libstdc++-v3/include/std/stack index 5cea4762a192..a57a5a08bc3c 100644 --- a/libstdc++-v3/include/std/stack +++ b/libstdc++-v3/include/std/stack @@ -61,11 +61,53 @@ #include // containers -#include -#include - #define __glibcxx_want_adaptor_iterator_pair_constructor #define __glibcxx_want_containers_ranges #include +#include +#include + +#ifdef __glibcxx_format_ranges // C++ >= 23 && HOSTED +#include + +namespace std _GLIBCXX_VISIBILITY(default) +{ +_GLIBCXX_BEGIN_NAMESPACE_VERSION + // Standard does not constrain accepted _CharT, we do so we can + // befriend specializations. + template<__format::__char _CharT, typename _Tp, + formattable<_CharT> _Container> + struct formatter, _CharT> + { + private: + using __maybe_const_adaptor + = __conditional_t< + __format::__const_formattable_range<_Container, _CharT>, + const stack<_Tp, _Container>, stack<_Tp, _Container>>; + + public: + // Standard declares this as template accepting unconstrained + // ParseContext type. + constexpr typename basic_format_parse_context<_CharT>::iterator + parse(basic_format_parse_context<_CharT>& __pc) + { return _M_f.parse(__pc); } + + // Standard declares this as template accepting unconstrained + // FormatContext type. + template + typename basic_format_context<_Out, _CharT>::iterator + format(__maybe_const_adaptor& __a, + basic_format_context<_Out, _CharT>& __fc) const + { return _M_f.format(__a.c, __fc); } + + private: + // Standard uses formatter, _CharT>. + range_formatter<_Tp, _CharT> _M_f; + }; +_GLIBCXX_END_NAMESPACE_VERSION +} // namespace std +#endif // __glibcxx_format_ranges + + #endif /* _GLIBCXX_STACK */ 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 000000000000..854c7eef5bdd --- /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(); +}