On Tue, 22 Apr 2025 at 12:19, Tomasz Kamiński <tkami...@redhat.com> wrote:
>
> 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<ranges::ref_view<const? _Container>, 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<std::ref_view<PD>> 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<basic_string<_CharT>>, 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<queue<_Tp, _Container, _Compare>, _CharT>)
>         (formatter<priority_queue<_Tp, _Container, _Compare>, _CharT>): 
> Define.
>         * include/std/stack: Include bits/version.h before bits/stl_stack.h
>         (formatter<stack<_Tp, _Container, _Compare>, _CharT>): Define.
>         * testsuite/std/format/ranges/adaptors.cc: New test.
>
> Reviewed-by: Jonathan Wakely <jwak...@redhat.com>
> Signed-off-by: Tomasz Kamiński <tkami...@redhat.com>
> ---
> Applied changes to feature test macros. Also in queue/stack files
> included bits/version.h before bits/stl_queue.h.
>
> OK for trunk?

Looks good, but as it moves things around between headers let's wait
until after 15.1 is released. That way the contents of
<bits/formatfwd.h> won't diverge too much between trunk and gcc-15.

After 15.1 is released, this is OK for trunk and for gcc-15 (so that
15.2 will have full range formatting support).


>  libstdc++-v3/include/bits/formatfwd.h         |  78 +++++++++
>  libstdc++-v3/include/bits/stl_queue.h         |  14 ++
>  libstdc++-v3/include/bits/stl_stack.h         |   9 +
>  libstdc++-v3/include/std/format               |  76 +--------
>  libstdc++-v3/include/std/queue                |  80 ++++++++-
>  libstdc++-v3/include/std/stack                |  48 +++++-
>  .../testsuite/std/format/ranges/adaptors.cc   | 156 ++++++++++++++++++
>  7 files changed, 387 insertions(+), 74 deletions(-)
>  create mode 100644 libstdc++-v3/testsuite/std/format/ranges/adaptors.cc
>
> diff --git a/libstdc++-v3/include/bits/formatfwd.h 
> b/libstdc++-v3/include/bits/formatfwd.h
> index a6b5ac8c8ce..9ba658b078a 100644
> --- a/libstdc++-v3/include/bits/formatfwd.h
> +++ b/libstdc++-v3/include/bits/formatfwd.h
> @@ -37,6 +37,12 @@
>  // <bits/version.h> must have been included before this header:
>  #ifdef __glibcxx_format // C++ >= 20 && HOSTED
>
> +#include <concepts>
> +#include <type_traits>
> +#if __glibcxx_format_ranges // C++ >= 23 && HOSTED
> +#  include <bits/ranges_base.h>  // 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<typename _Tp, typename _CharT = char> 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 _Tp, typename _Context,
> +          typename _Formatter
> +            = typename _Context::template 
> formatter_type<remove_const_t<_Tp>>,
> +          typename _ParseContext
> +            = basic_format_parse_context<typename _Context::char_type>>
> +    concept __parsable_with
> +      = semiregular<_Formatter>
> +         && requires (_Formatter __f, _ParseContext __pc)
> +    {
> +      { __f.parse(__pc) } -> same_as<typename _ParseContext::iterator>;
> +    };
> +
> +  template<typename _Tp, typename _Context,
> +          typename _Formatter
> +            = typename _Context::template 
> formatter_type<remove_const_t<_Tp>>,
> +          typename _ParseContext
> +            = basic_format_parse_context<typename _Context::char_type>>
> +    concept __formattable_with
> +      = semiregular<_Formatter>
> +         && requires (const _Formatter __cf, _Tp&& __t, _Context __fc)
> +    {
> +      { __cf.format(__t, __fc) } -> same_as<typename _Context::iterator>;
> +    };
> +
> +  // An unspecified output iterator type used in the `formattable` concept.
> +  template<typename _CharT>
> +    struct _Iter_for;
> +  template<typename _CharT>
> +    using _Iter_for_t = typename _Iter_for<_CharT>::type;
> +
> +  template<typename _Tp, typename _CharT,
> +          typename _Context = basic_format_context<_Iter_for_t<_CharT>, 
> _CharT>>
> +    concept __formattable_impl
> +      = __parsable_with<_Tp, _Context> && __formattable_with<_Tp, _Context>;
> +
> +  template<typename _Formatter>
> +    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<typename _Tp, typename _CharT>
> +    concept formattable
> +      = __format::__formattable_impl<remove_reference_t<_Tp>, _CharT>;
> +
> +   template<typename _Tp, __format::__char _CharT = char>
> +     requires same_as<remove_cvref_t<_Tp>, _Tp> && formattable<_Tp, _CharT>
> +     class range_formatter;
> +
> +/// @cond undocumented
> +namespace __format
> +{
> +  template<typename _Rg, typename _CharT>
> +    concept __const_formattable_range
> +      = ranges::input_range<const _Rg>
> +         && formattable<ranges::range_reference_t<const _Rg>, _CharT>;
> +
> +  template<typename _Rg, typename _CharT>
> +    using __maybe_const_range
> +      = __conditional_t<__const_formattable_range<_Rg, _CharT>, const _Rg, 
> _Rg>;
> +
> +  template<typename _Tp, typename _CharT>
> +    using __maybe_const
> +      = __conditional_t<formattable<const _Tp, _CharT>, 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 554e076aae9..a3a8bc1f0ad 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<typename, typename> 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<queue<_Tp, _Sequence>, char>;
> +      friend class formatter<queue<_Tp, _Sequence>, 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<priority_queue<_Tp, _Sequence, _Compare>, char>;
> +      friend class formatter<priority_queue<_Tp, _Sequence, _Compare>, 
> 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 7b324642b32..27c79d6ce58 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<typename, typename> 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<stack<_Tp, _Sequence>, char>;
> +      friend class formatter<stack<_Tp, _Sequence>, wchar_t>;
> +#endif
>      };
>
>  #if __cpp_deduction_guides >= 201606
> diff --git a/libstdc++-v3/include/std/format b/libstdc++-v3/include/std/format
> index e557e104d74..7d3067098be 100644
> --- a/libstdc++-v3/include/std/format
> +++ b/libstdc++-v3/include/std/format
> @@ -117,6 +117,11 @@ namespace __format
>    template<typename _CharT>
>      class _Sink_iter;
>
> +  // An unspecified output iterator type used in the `formattable` concept.
> +  template<typename _CharT>
> +    struct _Iter_for
> +    { using type = back_insert_iterator<basic_string<_CharT>>; };
> +
>    template<typename _CharT>
>      using __format_context = basic_format_context<_Sink_iter<_CharT>, 
> _CharT>;
>
> @@ -135,6 +140,7 @@ namespace __format
>
>        template<typename, typename...> 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 _Tp, typename _Context,
> -          typename _Formatter
> -            = typename _Context::template 
> formatter_type<remove_const_t<_Tp>>,
> -          typename _ParseContext
> -            = basic_format_parse_context<typename _Context::char_type>>
> -    concept __parsable_with
> -      = semiregular<_Formatter>
> -         && requires (_Formatter __f, _ParseContext __pc)
> -    {
> -      { __f.parse(__pc) } -> same_as<typename _ParseContext::iterator>;
> -    };
> -
> -  template<typename _Tp, typename _Context,
> -          typename _Formatter
> -            = typename _Context::template 
> formatter_type<remove_const_t<_Tp>>,
> -          typename _ParseContext
> -            = basic_format_parse_context<typename _Context::char_type>>
> -    concept __formattable_with
> -      = semiregular<_Formatter>
> -         && requires (const _Formatter __cf, _Tp&& __t, _Context __fc)
> -    {
> -      { __cf.format(__t, __fc) } -> same_as<typename _Context::iterator>;
> -    };
> -
> -  // An unspecified output iterator type used in the `formattable` concept.
> -  template<typename _CharT>
> -    using _Iter_for = back_insert_iterator<basic_string<_CharT>>;
> -
> -  template<typename _Tp, typename _CharT,
> -          typename _Context = basic_format_context<_Iter_for<_CharT>, 
> _CharT>>
> -    concept __formattable_impl
> -      = __parsable_with<_Tp, _Context> && __formattable_with<_Tp, _Context>;
> -
> -  template<typename _Formatter>
> -    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<typename _Tp, typename _CharT>
> -    concept formattable
> -      = __format::__formattable_impl<remove_reference_t<_Tp>, _CharT>;
> -
> -#endif // format_ranges
> -
>    /// An iterator after the last character written, and the number of
>    /// characters that would have been written.
>    template<typename _Out>
> @@ -5250,26 +5203,13 @@ namespace __format
>        return __format::__write_padded_as_spec(__str, __width, __fc, __spec);
>      }
>
> -  template<typename _Rg, typename _CharT>
> -    concept __const_formattable_range
> -      = ranges::input_range<const _Rg>
> -         && formattable<ranges::range_reference_t<const _Rg>, _CharT>;
> -
>    // _Rg& and const _Rg& are both formattable and use same formatter
>    // specialization for their references.
>    template<typename _Rg, typename _CharT>
>      concept __simply_formattable_range
>        = __const_formattable_range<_Rg, _CharT>
>           && same_as<remove_cvref_t<ranges::range_reference_t<_Rg>>,
> -                    remove_cvref_t<ranges::range_reference_t<const _Rg>>>;
> -
> -  template<typename _Rg, typename _CharT>
> -    using __maybe_const_range
> -      = __conditional_t<__const_formattable_range<_Rg, _CharT>, const _Rg, 
> _Rg>;
> -
> -  template<typename _Tp, typename _CharT>
> -    using __maybe_const
> -      = __conditional_t<formattable<const _Tp, _CharT>, const _Tp, _Tp>;
> +                    remove_cvref_t<ranges::range_reference_t<const _Rg>>>;
>
>    template<size_t _Pos, typename _Tp, typename _CharT>
>      struct __indexed_formatter_storage
> @@ -5493,7 +5433,7 @@ namespace __format
>      };
>
>    // [format.range.formatter], class template range_formatter
> -  template<typename _Tp, __format::__char _CharT = char>
> +  template<typename _Tp, __format::__char _CharT>
>      requires same_as<remove_cvref_t<_Tp>, _Tp> && formattable<_Tp, _CharT>
>      class range_formatter
>      {
> diff --git a/libstdc++-v3/include/std/queue b/libstdc++-v3/include/std/queue
> index 74b6c07b49f..90525897da7 100644
> --- a/libstdc++-v3/include/std/queue
> +++ b/libstdc++-v3/include/std/queue
> @@ -61,14 +61,88 @@
>
>  #include <bits/requires_hosted.h> // containers
>
> +#define __glibcxx_want_adaptor_iterator_pair_constructor
> +#define __glibcxx_want_containers_ranges
> +#include <bits/version.h>
> +
>  #include <deque>
>  #include <vector>
>  #include <bits/stl_heap.h>
>  #include <bits/stl_function.h>
>  #include <bits/stl_queue.h>
>
> -#define __glibcxx_want_adaptor_iterator_pair_constructor
> -#define __glibcxx_want_containers_ranges
> -#include <bits/version.h>
> +#ifdef __glibcxx_format_ranges // C++ >= 23 && HOSTED
> +#include <bits/formatfwd.h>
> +
> +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<queue<_Tp, _Container>, _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 _Out>
> +       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<ref_view<_Container>, _CharT>.
> +      range_formatter<_Tp, _CharT> _M_f;
> +    };
> +
> +  template<__format::__char _CharT, typename _Tp,
> +          formattable<_CharT> _Container, typename _Compare>
> +    struct formatter<priority_queue<_Tp, _Container, _Compare>, _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 _Out>
> +       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<ref_view<_Container>, _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 5cea4762a19..a57a5a08bc3 100644
> --- a/libstdc++-v3/include/std/stack
> +++ b/libstdc++-v3/include/std/stack
> @@ -61,11 +61,53 @@
>
>  #include <bits/requires_hosted.h> // containers
>
> -#include <deque>
> -#include <bits/stl_stack.h>
> -
>  #define __glibcxx_want_adaptor_iterator_pair_constructor
>  #define __glibcxx_want_containers_ranges
>  #include <bits/version.h>
>
> +#include <deque>
> +#include <bits/stl_stack.h>
> +
> +#ifdef __glibcxx_format_ranges // C++ >= 23 && HOSTED
> +#include <bits/formatfwd.h>
> +
> +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<stack<_Tp, _Container>, _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 _Out>
> +       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<ref_view<_Container>, _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 00000000000..854c7eef5bd
> --- /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 <format>
> +#include <queue>
> +#include <stack>
> +#include <testsuite_hooks.h>
> +
> +template<typename... Args>
> +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<C>(S, L##S)
> +#define WIDEN(S) WIDEN_(_CharT, S)
> +
> +template<template<typename Tp> class Adaptor>
> +void
> +test_format_string()
> +{
> +  Adaptor<int> 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<typename CharT>
> +struct std::formatter<MutFormat, CharT>
> +  : std::formatter<int, CharT>
> +{
> +   template<typename Out>
> +   Out format(MutFormat& mf, basic_format_context<Out, CharT>& ctx) const
> +   { return std::formatter<int, CharT>::format(mf.x, ctx); }
> +};
> +
> +template<typename T>
> +struct NotFormattableCont : std::vector<T>
> +{
> +  using std::vector<T>::vector;
> +};
> +
> +template<typename T>
> +constexpr auto std::format_kind<NotFormattableCont<T>>
> +  = std::range_format::disabled;
> +
> +template<typename _CharT,
> +        template<typename Tp, typename Cont = std::vector<Tp>> class Adaptor>
> +void
> +test_output()
> +{
> +  const std::vector<int> v{3, 2, 1};
> +  std::basic_string<_CharT> res;
> +  Adaptor<int, std::vector<int>> 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<int, std::deque<int>> 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<MutFormat> mq(std::from_range, v);
> +
> +  res = std::format(WIDEN("{}"), mq);
> +  VERIFY( res == WIDEN("[3, 2, 1]") );
> +
> +  static_assert(!std::formattable<const Adaptor<MutFormat>, _CharT>);
> +
> +  static_assert(!std::formattable<Adaptor<NoFormat>, _CharT>);
> +  static_assert(!std::formattable<const Adaptor<NoFormat>, _CharT>);
> +
> +  // Formatter check if container is formattable, not container elements.
> +  static_assert(!std::formattable<Adaptor<int, NotFormattableCont<int>>, 
> _CharT>);
> +}
> +
> +template<template<typename Tp, typename Cont = std::vector<Tp>> class 
> Adaptor>
> +void
> +test_adaptor()
> +{
> +  test_format_string<Adaptor>();
> +  test_output<char, Adaptor>();
> +  test_output<wchar_t, Adaptor>();
> +
> +  static_assert(!std::formattable<Adaptor<int>, int>);
> +  static_assert(!std::formattable<Adaptor<int>, char32_t>);
> +}
> +
> +template<typename _CharT>
> +void
> +test_compare()
> +{
> +  const std::vector<int> v{3, 2, 1};
> +  std::basic_string<_CharT> res;
> +  std::priority_queue<int, std::vector<int>, std::greater<>> q(
> +     std::from_range, v);
> +
> +  res = std::format(WIDEN("{}"), q);
> +  VERIFY( res == WIDEN("[1, 2, 3]") );
> +}
> +
> +int main()
> +{
> +  test_adaptor<std::queue>();
> +  test_adaptor<std::priority_queue>();
> +  test_compare<char>();
> +}
> --
> 2.49.0
>

Reply via email to