This patch implement formatter for vector<bool>::reference which
is part of P2286R8.

To indicate partial support we define __glibcxx_format_ranges macro
value 1, without defining __cpp_lib_format_ranges.

To avoid including the whole content of the <format> header, we
introduce new bits/formatfwd.h forward declares classes required
for newly introduce formatter.

The signatures of the user-facing parse and format method of the provided
formatters deviate from the standard by constraining types of params:
* _Bit_reference instead T satisfying is-vector-bool-reference<T>
* _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 last three of above as unconstrained types, which leads
to formattable<vector<bool>::reference, char32_t> (and any other type as char)
being true.

        PR libstdc++/109162

libstdc++-v3/ChangeLog:

        * include/Makefile.am: Add bits/formatfwd.h.
        * include/Makefile.in: Add bits/formatfwd.h.
        * include/bits/version.def:
        Define __glibcxx_format_ranges without corresponding std name.
        * include/bits/version.h: Regenerate.
        * include/std/format (basic_format_context, __format::__char):
        Move declartions to bits/formatfwd.h.
        (formatter<_Tp, _CharT>): Remove default argument for _CharT
        parameter, now specified in forward declaration in bits/formatfwd.h.
        * include/std/vector (formatter<_Bit_reference, _CharT>: Define.
        * include/bits/formatfwd.h: New file with forward declartions
        for bits of std/format.
        * testsuite/23_containers/vector/bool/format.cc: New test.
---
Testing for x86_64-linux, format tests passed.
OK for trunk if all test passes?
I think I could land this first to bring __glibcxx_format_ranges macro.

 libstdc++-v3/include/Makefile.am              |  1 +
 libstdc++-v3/include/Makefile.in              |  1 +
 libstdc++-v3/include/bits/formatfwd.h         | 68 +++++++++++++++++++
 libstdc++-v3/include/bits/version.def         | 18 ++---
 libstdc++-v3/include/bits/version.h           | 10 +++
 libstdc++-v3/include/std/format               | 14 +---
 libstdc++-v3/include/std/vector               | 30 ++++++++
 .../23_containers/vector/bool/format.cc       | 67 ++++++++++++++++++
 8 files changed, 188 insertions(+), 21 deletions(-)
 create mode 100644 libstdc++-v3/include/bits/formatfwd.h
 create mode 100644 libstdc++-v3/testsuite/23_containers/vector/bool/format.cc

diff --git a/libstdc++-v3/include/Makefile.am b/libstdc++-v3/include/Makefile.am
index 4dc771a540c..537774c2668 100644
--- a/libstdc++-v3/include/Makefile.am
+++ b/libstdc++-v3/include/Makefile.am
@@ -195,6 +195,7 @@ bits_headers = \
        ${bits_srcdir}/cow_string.h \
        ${bits_srcdir}/deque.tcc \
        ${bits_srcdir}/erase_if.h \
+       ${bits_srcdir}/formatfwd.h \
        ${bits_srcdir}/forward_list.h \
        ${bits_srcdir}/forward_list.tcc \
        ${bits_srcdir}/fs_dir.h \
diff --git a/libstdc++-v3/include/Makefile.in b/libstdc++-v3/include/Makefile.in
index 0e3d09b3a75..7b96b2207f8 100644
--- a/libstdc++-v3/include/Makefile.in
+++ b/libstdc++-v3/include/Makefile.in
@@ -548,6 +548,7 @@ bits_freestanding = \
 @GLIBCXX_HOSTED_TRUE@  ${bits_srcdir}/cow_string.h \
 @GLIBCXX_HOSTED_TRUE@  ${bits_srcdir}/deque.tcc \
 @GLIBCXX_HOSTED_TRUE@  ${bits_srcdir}/erase_if.h \
+@GLIBCXX_HOSTED_TRUE@  ${bits_srcdir}/formatfwd.h \
 @GLIBCXX_HOSTED_TRUE@  ${bits_srcdir}/forward_list.h \
 @GLIBCXX_HOSTED_TRUE@  ${bits_srcdir}/forward_list.tcc \
 @GLIBCXX_HOSTED_TRUE@  ${bits_srcdir}/fs_dir.h \
diff --git a/libstdc++-v3/include/bits/formatfwd.h 
b/libstdc++-v3/include/bits/formatfwd.h
new file mode 100644
index 00000000000..5450ad1297f
--- /dev/null
+++ b/libstdc++-v3/include/bits/formatfwd.h
@@ -0,0 +1,68 @@
+// <format> Formatting -*- C++ -*-
+
+// Copyright The GNU Toolchain Authors.
+//
+// This file is part of the GNU ISO C++ Library.  This library is free
+// software; you can redistribute it and/or modify it under the
+// terms of the GNU General Public License as published by the
+// Free Software Foundation; either version 3, or (at your option)
+// any later version.
+
+// This library is distributed in the hope that it will be useful,
+// but WITHOUT ANY WARRANTY; without even the implied warranty of
+// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+// GNU General Public License for more details.
+
+// Under Section 7 of GPL version 3, you are granted additional
+// permissions described in the GCC Runtime Library Exception, version
+// 3.1, as published by the Free Software Foundation.
+
+// You should have received a copy of the GNU General Public License and
+// a copy of the GCC Runtime Library Exception along with this program;
+// see the files COPYING3 and COPYING.RUNTIME respectively.  If not, see
+// <http://www.gnu.org/licenses/>.
+
+/** @file include/format
+ *  This is a Standard C++ Library header.
+ */
+
+#ifndef _GLIBCXX_FORMAT_FWD_H
+#define _GLIBCXX_FORMAT_FWD_H 1
+
+#ifdef _GLIBCXX_SYSHDR
+#pragma GCC system_header
+#endif
+#ifdef __glibcxx_format // C++ >= 20 && HOSTED
+
+namespace std _GLIBCXX_VISIBILITY(default)
+{
+_GLIBCXX_BEGIN_NAMESPACE_VERSION
+
+  // [format.context], class template basic_format_context
+  template<typename _Out, typename _CharT> class basic_format_context;
+
+  // [format.parse.ctx], class template basic_format_parse_context
+  template<typename _CharT> class basic_format_parse_context;
+
+  // [format.formatter], formatter
+  template<typename _Tp, typename _CharT = char> struct formatter;
+
+namespace __format
+{
+#ifdef _GLIBCXX_USE_WCHAR_T
+  template<typename _CharT>
+    concept __char = same_as<_CharT, char> || same_as<_CharT, wchar_t>;
+#else
+  template<typename _CharT>
+    concept __char = same_as<_CharT, char>;
+#endif
+
+  template<__char _CharT>
+    struct __formatter_int;
+}
+
+_GLIBCXX_END_NAMESPACE_VERSION
+} // namespace std
+#endif // __glibcxx_format
+#pragma GCC diagnostic pop
+#endif // _GLIBCXX_FORMAT_FWD_H
diff --git a/libstdc++-v3/include/bits/version.def 
b/libstdc++-v3/include/bits/version.def
index 1468c0491b7..d7621431762 100644
--- a/libstdc++-v3/include/bits/version.def
+++ b/libstdc++-v3/include/bits/version.def
@@ -1404,18 +1404,18 @@ ftms = {
   };
 };
 
-// ftms = {
-  // name = format_ranges;
+ftms = {
+  name = format_ranges;
   // 202207 P2286R8 Formatting Ranges
   // 202207 P2585R1 Improving default container formatting
   // LWG3750 Too many papers bump __cpp_lib_format
-  // TODO: #define __cpp_lib_format_ranges 202207L
-  // values = {
-    // v = 202207;
-    // cxxmin = 23;
-    // hosted = yes;
-  // };
-// };
+  stdname = __glibcxx_format_ranges_part; // TODO remove
+  values = {
+    v = 1; // TODO 202207
+    cxxmin = 23;
+    hosted = yes;
+  };
+};
 
 ftms = {
   name = freestanding_algorithm;
diff --git a/libstdc++-v3/include/bits/version.h 
b/libstdc++-v3/include/bits/version.h
index f7c9849893d..f51bf38418d 100644
--- a/libstdc++-v3/include/bits/version.h
+++ b/libstdc++-v3/include/bits/version.h
@@ -1555,6 +1555,16 @@
 #endif /* !defined(__cpp_lib_expected) && defined(__glibcxx_want_expected) */
 #undef __glibcxx_want_expected
 
+#if !defined(__cpp_lib_format_ranges)
+# if (__cplusplus >= 202100L) && _GLIBCXX_HOSTED
+#  define __glibcxx_format_ranges 1L
+#  if defined(__glibcxx_want_all) || defined(__glibcxx_want_format_ranges)
+#   define __glibcxx_format_ranges_part 1L
+#  endif
+# endif
+#endif /* !defined(__cpp_lib_format_ranges) && 
defined(__glibcxx_want_format_ranges) */
+#undef __glibcxx_want_format_ranges
+
 #if !defined(__cpp_lib_freestanding_algorithm)
 # if (__cplusplus >= 202100L)
 #  define __glibcxx_freestanding_algorithm 202311L
diff --git a/libstdc++-v3/include/std/format b/libstdc++-v3/include/std/format
index 9ef719edcf0..01a53143d1c 100644
--- a/libstdc++-v3/include/std/format
+++ b/libstdc++-v3/include/std/format
@@ -52,6 +52,7 @@
 #include <string_view>
 #include <string>
 #include <bits/monostate.h>
+#include <bits/formatfwd.h>
 #include <bits/ranges_base.h>  // input_range, range_reference_t
 #include <bits/ranges_util.h>  // subrange
 #include <bits/ranges_algobase.h> // ranges::copy
@@ -73,9 +74,6 @@ namespace std _GLIBCXX_VISIBILITY(default)
 {
 _GLIBCXX_BEGIN_NAMESPACE_VERSION
 
-  // [format.context], class template basic_format_context
-  template<typename _Out, typename _CharT> class basic_format_context;
-
   // [format.fmt.string], class template basic_format_string
   template<typename _CharT, typename... _Args> struct basic_format_string;
 
@@ -178,7 +176,7 @@ namespace __format
   // [format.formatter], formatter
 
   /// The primary template of std::formatter is disabled.
-  template<typename _Tp, typename _CharT = char>
+  template<typename _Tp, typename _CharT>
     struct formatter
     {
       formatter() = delete; // No std::formatter specialization for this type.
@@ -923,14 +921,6 @@ namespace __format
     bool _M_hasval = false;
   };
 
-#ifdef _GLIBCXX_USE_WCHAR_T
-  template<typename _CharT>
-    concept __char = same_as<_CharT, char> || same_as<_CharT, wchar_t>;
-#else
-  template<typename _CharT>
-    concept __char = same_as<_CharT, char>;
-#endif
-
   template<__char _CharT>
     struct __formatter_str
     {
diff --git a/libstdc++-v3/include/std/vector b/libstdc++-v3/include/std/vector
index 0f043340fe5..e21f5522cd8 100644
--- a/libstdc++-v3/include/std/vector
+++ b/libstdc++-v3/include/std/vector
@@ -157,4 +157,34 @@ _GLIBCXX_END_NAMESPACE_VERSION
 } // namespace std
 #endif // __cpp_lib_erase_if
 
+#ifdef __glibcxx_format_ranges // C++ >= 20 && HOSTED
+#include <bits/formatfwd.h>
+
+namespace std _GLIBCXX_VISIBILITY(default)
+{
+  // Standard does not constrain accepted _CharT and declares it as formatter
+  // of Tp that statisfies is-vector-bool-reference<T>,
+  template<__format::__char _CharT>
+    struct formatter<_Bit_reference, _CharT> {
+    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.template _M_parse<bool>(__pc); }
+
+      // Standard declares this as template accepting unconstrained 
+      // FormatContext type.
+      template<typename _Out>
+       typename basic_format_context<_Out, _CharT>::iterator
+       format(const _Bit_reference& __u,
+              basic_format_context<_Out, _CharT>& __fc) const
+       { return _M_f.format(static_cast<bool>(__u), __fc); }
+
+    private:
+      __format::__formatter_int<_CharT> _M_f;
+    };
+} // namespace std
+#endif // __glibcxx_format_ranges
+
 #endif /* _GLIBCXX_VECTOR */
diff --git a/libstdc++-v3/testsuite/23_containers/vector/bool/format.cc 
b/libstdc++-v3/testsuite/23_containers/vector/bool/format.cc
new file mode 100644
index 00000000000..1935d06ff88
--- /dev/null
+++ b/libstdc++-v3/testsuite/23_containers/vector/bool/format.cc
@@ -0,0 +1,67 @@
+// { dg-do run { target c++23 } }
+// { dg-timeout-factor 2 }
+
+#include <format>
+#include <vector>
+#include <chrono> // For _Widen
+#include <testsuite_hooks.h>
+
+static_assert(!std::formattable<std::vector<bool>::reference, int>);
+static_assert(!std::formattable<std::vector<bool>::reference, char32_t>);
+
+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::chrono::__detail::_Widen<C>(S, L##S)
+#define WIDEN(S) WIDEN_(_CharT, S)
+
+void
+test_format_string()
+{
+  std::vector<bool> v(1, true);
+  VERIFY( !is_format_string_for("{:?}", v[0]) );
+  VERIFY( !is_format_string_for("{:P}", v[0]) );
+
+  // width needs to integer type
+  VERIFY( !is_format_string_for("{:{}}", v[0], 1.0f) );
+}
+
+template<typename _CharT>
+void
+test_output()
+{
+  std::basic_string<_CharT> res;
+  size_t size = 0;
+  std::vector<bool> v{true, false};
+
+  res = std::format(WIDEN("{}"), v[0]);
+  VERIFY( res == WIDEN("true") );
+
+  res = std::format(WIDEN("{:s}"), v[1]);
+  VERIFY( res == WIDEN("false") );
+
+  res = std::format(WIDEN("{:d} {:#B} {:#o} {:#x}"), v[0], v[1], v[0], v[1]);
+  VERIFY( res == WIDEN("1 0B0 01 0x0") );
+
+  res = std::format(WIDEN("{:{}}"), v[0], 6);
+  VERIFY( res == WIDEN("true  ") );
+
+  res = std::format(WIDEN("{:=^#7X}"), v[1]);
+  VERIFY( res == WIDEN("==0X0==") );
+}
+
+int main()
+{
+  test_format_string();
+  test_output<char>();
+  test_output<wchar_t>();
+}
-- 
2.48.1

Reply via email to