]> git.ipfire.org Git - thirdparty/gcc.git/commitdiff
libstdc++: construct bitset from string_view (P2697) [PR119742]
authorNathan Myers <ncm@cantrip.org>
Mon, 30 Jun 2025 22:55:48 +0000 (18:55 -0400)
committerNathan Myers <ncm@cantrip.org>
Thu, 3 Jul 2025 11:46:37 +0000 (07:46 -0400)
Add a bitset constructor from string_view, per P2697. Fix existing
tests that would fail to detect incorrect exception behavior.

Argument checks that result in exceptions guarded by "#if HOSTED"
are made unguarded because the functions called to throw just call
terminate() in free-standing builds. Improve readability in Doxygen
comments. Generalize a private member argument-checking function
to work with string and string_view without mentioning either,
obviating need for guards.

The version.h symbol is not "hosted" because string_view, though
not specified to be available in free-standing builds, is defined
there and the feature is useful there.

libstdc++-v3/ChangeLog:
PR libstdc++/119742
* include/bits/version.def: Add preprocessor symbol.
* include/bits/version.h: Add preprocessor symbol.
* include/std/bitset: Add constructor.
* testsuite/20_util/bitset/cons/1.cc: Fix.
* testsuite/20_util/bitset/cons/6282.cc: Fix.
* testsuite/20_util/bitset/cons/string_view.cc: Test new ctor.
* testsuite/20_util/bitset/cons/string_view_wide.cc: Test new ctor.

libstdc++-v3/include/bits/version.def
libstdc++-v3/include/bits/version.h
libstdc++-v3/include/std/bitset
libstdc++-v3/testsuite/20_util/bitset/cons/1.cc
libstdc++-v3/testsuite/20_util/bitset/cons/6282.cc
libstdc++-v3/testsuite/20_util/bitset/cons/string_view.cc [new file with mode: 0644]
libstdc++-v3/testsuite/20_util/bitset/cons/string_view_wide.cc [new file with mode: 0644]

index f4ba501c403ff80cdd55d087a4c2dabbdb9ee09e..b89b287e8e8f39902e727ff4ace0b40f35830c75 100644 (file)
@@ -2030,6 +2030,14 @@ ftms = {
   };
 };
 
+ftms = {
+  name = bitset  // ...construct from string_view
+  values = {
+    v = 202306;
+    cxxmin = 26;
+  };
+};
+
 // Standard test specifications.
 stds[97] = ">= 199711L";
 stds[03] = ">= 199711L";
index dc8ac07be166a5ebe35d088b03f3c2a3e6fb287f..a70a7ede68cffa3eb90ebae3862800d049bde542 100644 (file)
 #endif /* !defined(__cpp_lib_exception_ptr_cast) && defined(__glibcxx_want_exception_ptr_cast) */
 #undef __glibcxx_want_exception_ptr_cast
 
+#if !defined(__cpp_lib_bitset)
+# if (__cplusplus >  202302L)
+#  define __glibcxx_bitset 202306L
+#  if defined(__glibcxx_want_all) || defined(__glibcxx_want_bitset)
+#   define __cpp_lib_bitset 202306L
+#  endif
+# endif
+#endif /* !defined(__cpp_lib_bitset) && defined(__glibcxx_want_bitset) */
+#undef __glibcxx_bitset
+
 #undef __glibcxx_want_all
index 8b5d270c2a94281ff93225e6c0f26961d433ee3a..1c1e1670c33f16b8db61c5a219d1519f9681b1ce 100644 (file)
 #endif
 
 #define __glibcxx_want_constexpr_bitset
+#define __glibcxx_want_bitset  // ...construct from string_view
 #include <bits/version.h>
 
+#ifdef __cpp_lib_bitset // ...construct from string_view
+# include <string_view>
+#endif
+
 #define _GLIBCXX_BITSET_BITS_PER_WORD  (__CHAR_BIT__ * __SIZEOF_LONG__)
 #define _GLIBCXX_BITSET_WORDS(__n) \
   ((__n) / _GLIBCXX_BITSET_BITS_PER_WORD + \
@@ -752,7 +757,7 @@ _GLIBCXX_BEGIN_NAMESPACE_CONTAINER
    *  (Note that %bitset does @e not meet the formal requirements of a
    *  <a href="tables.html#65">container</a>.  Mainly, it lacks iterators.)
    *
-   *  The template argument, @a Nb, may be any non-negative number,
+   *  The template argument, `Nb`, may be any non-negative number,
    *  specifying the number of bits (e.g., "0", "12", "1024*1024").
    *
    *  In the general unoptimized case, storage is allocated in word-sized
@@ -816,28 +821,25 @@ _GLIBCXX_BEGIN_NAMESPACE_CONTAINER
       typedef _Base_bitset<_GLIBCXX_BITSET_WORDS(_Nb)> _Base;
       typedef unsigned long _WordT;
 
-#if _GLIBCXX_HOSTED
-      template<class _CharT, class _Traits, class _Alloc>
-      _GLIBCXX23_CONSTEXPR
-      void
-      _M_check_initial_position(const std::basic_string<_CharT, _Traits, _Alloc>& __s,
-                               size_t __position) const
+      template<class _Str>
+      _GLIBCXX23_CONSTEXPR void
+      _M_check_initial_position(
+       const _Str& __s, typename _Str::size_type __position) const
       {
        if (__position > __s.size())
-         __throw_out_of_range_fmt(__N("bitset::bitset: __position "
-                                      "(which is %zu) > __s.size() "
-                                      "(which is %zu)"),
-                                  __position, __s.size());
+         __throw_out_of_range_fmt(
+           __N("bitset::bitset:"
+               " __position (which is %zu) > __s.size() (which is %zu)"),
+           size_t(__position), size_t(__s.size()));
       }
-#endif // HOSTED
 
       _GLIBCXX23_CONSTEXPR
       void _M_check(size_t __position, const char *__s) const
       {
        if (__position >= _Nb)
-         __throw_out_of_range_fmt(__N("%s: __position (which is %zu) "
-                                      ">= _Nb (which is %zu)"),
-                                  __s, __position, _Nb);
+         __throw_out_of_range_fmt(
+           __N("%s: __position (which is %zu) >= _Nb (which is %zu)"),
+           __s, size_t(__position), size_t(_Nb));
       }
 
       _GLIBCXX23_CONSTEXPR
@@ -954,12 +956,12 @@ _GLIBCXX_BEGIN_NAMESPACE_CONTAINER
 #if _GLIBCXX_HOSTED
       /**
        *  Use a subset of a string.
-       *  @param  __s  A string of @a 0 and @a 1 characters.
-       *  @param  __position  Index of the first character in @a __s to use;
+       *  @param  __s  A string of `0` and `1` characters.
+       *  @param  __position  Index of the first character in `__s` to use;
        *                    defaults to zero.
-       *  @throw  std::out_of_range  If @a pos is bigger the size of @a __s.
+       *  @throw  std::out_of_range  If `__position > __s.size()`.
        *  @throw  std::invalid_argument  If a character appears in the string
-       *                                 which is neither @a 0 nor @a 1.
+       *                                 which is neither `0` nor `1`.
        */
       template<class _CharT, class _Traits, class _Alloc>
        _GLIBCXX23_CONSTEXPR
@@ -976,13 +978,12 @@ _GLIBCXX_BEGIN_NAMESPACE_CONTAINER
 
       /**
        *  Use a subset of a string.
-       *  @param  __s  A string of @a 0 and @a 1 characters.
-       *  @param  __position  Index of the first character in @a __s to use.
+       *  @param  __s  A string of `0` and `1` characters.
+       *  @param  __position  Index of the first character in `__s` to use.
        *  @param  __n    The number of characters to copy.
-       *  @throw std::out_of_range If @a __position is bigger the size
-       *  of @a __s.
+       *  @throw  std::out_of_range  If `__position > __s.size()`.
        *  @throw  std::invalid_argument  If a character appears in the string
-       *                                 which is neither @a 0 nor @a 1.
+       *                                 which is neither `0` nor `1`.
        */
       template<class _CharT, class _Traits, class _Alloc>
        _GLIBCXX23_CONSTEXPR
@@ -1008,15 +1009,42 @@ _GLIBCXX_BEGIN_NAMESPACE_CONTAINER
        }
 #endif // HOSTED
 
+#ifdef __cpp_lib_bitset
+      /**
+       *  Use a subset of a string view.
+       *  @param  __s  A `string_view` of a sequence of `0` and `1` characters.
+       *  @param  __position  Index of the first character in `__s` to use.
+       *  @param  __n    The maximum number of characters from `__s` to use.
+       *  @param  __zero The character corresponding to the value 0.
+       *  @param  __one  The character corresponding to the value 1.
+       *  @throw  std::out_of_range  If `__position > __s.size()`.
+       *  @throw  std::invalid_argument  If a character appears in `__s`
+       *                                 which is neither `0` nor `1`.
+       */
+      template<class _CharT, class _Traits>
+       constexpr explicit
+       bitset(basic_string_view<_CharT, _Traits> __s,
+         basic_string_view<_CharT, _Traits>::size_type __position = 0,
+         basic_string_view<_CharT, _Traits>::size_type __n =
+           basic_string_view<_CharT, _Traits>::npos,
+         _CharT __zero = _CharT('0'), _CharT __one = _CharT('1'))
+       : _Base()
+       {
+         _M_check_initial_position(__s, __position);
+         _M_copy_from_ptr<_CharT, _Traits>(
+           __s.data(), __s.size(), __position, __n, __zero, __one);
+       }
+#endif
+
 #if __cplusplus >= 201103L
       /**
        *  Construct from a character %array.
-       *  @param  __str  An %array of characters @a zero and @a one.
+       *  @param  __str  An %array of characters `__zero` and `__one`.
        *  @param  __n    The number of characters to use.
        *  @param  __zero The character corresponding to the value 0.
        *  @param  __one  The character corresponding to the value 1.
        *  @throw  std::invalid_argument If a character appears in the string
-       *                                which is neither @a __zero nor @a __one.
+       *                                which is neither `__zero` nor `__one`.
        */
       template<typename _CharT>
        [[__gnu__::__nonnull__]]
@@ -1028,10 +1056,8 @@ _GLIBCXX_BEGIN_NAMESPACE_CONTAINER
               _CharT __zero = _CharT('0'), _CharT __one = _CharT('1'))
         : _Base()
         {
-#if _GLIBCXX_HOSTED
          if (!__str)
            __throw_logic_error(__N("bitset::bitset(const _CharT*, ...)"));
-#endif
          using _Traits = typename __bitset::__string<_CharT>::traits_type;
 
          if (__n == __bitset::__string<_CharT>::npos)
index 0a40b6cd9658c1af780f2beb5ac67485c33d0fa6..6441332c8d62d373cde1201230dfb0d540e2dc46 100644 (file)
@@ -47,6 +47,7 @@ void test01(void)
   const size_t n3 = 128;
   try {
     std::bitset<n3> bit03(str01, 5);
+    VERIFY(false);
   }
   catch(std::invalid_argument& fail) {
     VERIFY( true );
index fafa9fc808bcf7ffe183d62869327ab1a3c865e1..ee348c35b2a3559955256be8c408b2829b3d0441 100644 (file)
@@ -39,6 +39,8 @@ void test02(void)
   std::bitset<0>  z3(std::string("10101010101"));
   VERIFY( z3.any() == false );
 
+  VERIFY( z1.to_ulong() == 0 );
+  VERIFY( (z1.to_string<char,char_traits<char>,allocator<char> >().empty() ));
   try {
     z1.set(0);
     VERIFY( false );
@@ -49,9 +51,6 @@ void test02(void)
   catch(...) {
     VERIFY( false );
   }
-
-  VERIFY( z1.to_ulong() == 0 );
-  VERIFY( (z1.to_string<char,char_traits<char>,allocator<char> >().empty() ));
 }
 
 int main()
diff --git a/libstdc++-v3/testsuite/20_util/bitset/cons/string_view.cc b/libstdc++-v3/testsuite/20_util/bitset/cons/string_view.cc
new file mode 100644 (file)
index 0000000..ec3a6c8
--- /dev/null
@@ -0,0 +1,132 @@
+// C++26 [bitset.cons]
+
+// { dg-do run { target c++26 } }
+
+#ifndef C
+# define C char
+# define L(s) s
+#endif
+
+#include <bitset>
+#include <string>
+#include <string_view>
+#include <algorithm> // std::reverse, std::transform
+#include <stdexcept>
+#include <testsuite_hooks.h>
+
+void test01()
+{
+  // template<_C,_T>
+  // constexpr explicit
+  // bitset(const basic_string_view<_C,_T>,
+  //   size_type pos, size_type n, _C zero, _C one)
+  try {
+    std::basic_string_view<C> str(L("stuff smith sessions"));
+    const std::size_t n = 128;
+    std::bitset<n> bit(str, 5);
+    VERIFY(false);
+  }
+  catch(std::invalid_argument& fail) {
+    VERIFY( true );
+  }
+  catch(...) {
+    VERIFY( false );
+  }
+
+  try {
+    std::basic_string_view<C> str(L("010101000011"));
+    const std::size_t n = 128;
+    const auto sz = str.size();
+    std::bitset<n> bit(str, 0);
+    std::basic_string<C> str02;
+    for (std::size_t i = 0; i < sz; ++i)
+      str02 += (bit.test(i) ? C('1') : C('0'));
+    std::reverse(str02.begin(), str02.end());
+    VERIFY( str02 == str );
+  }
+  catch(std::invalid_argument& fail) {
+    VERIFY( false );
+  }
+  catch(...) {
+    VERIFY( false );
+  }
+
+  // substring<C> "010101000011"
+  //              "10100001"
+  try {
+    std::basic_string_view<C> str(L("010101000011"));
+    const std::size_t n = 128;
+    const auto sz = 8;
+    std::bitset<n> bit(str, 3, sz);
+    std::basic_string<C> str02;
+    for (std::size_t i = 0; i < sz; ++i)
+      str02 += (bit.test(i) ? C('1') : C('0'));
+    std::reverse(str02.begin(), str02.end());
+    VERIFY( str02 == str.substr(3, sz) );
+  }
+  catch(std::invalid_argument& fail) {
+    VERIFY( false );
+  }
+  catch(...) {
+    VERIFY( false );
+  }
+
+  // "abababaaaabb", zero = 'a', one = 'b'
+  try {
+    std::basic_string_view<C> str(L("010101000011"));
+    const std::size_t n = 128;
+    const auto sz = str.size();
+    std::basic_string<C> str02(str);
+    std::transform(str02.cbegin(), str02.cend(), str02.begin(), [](auto c) {
+      return (c - C('0')) + C('a');
+    });
+    std::basic_string_view<C> str03(str02);
+    std::bitset<n> bit(str03, 0, 100, C('a'), C('b'));
+    std::basic_string<C> str04;
+    for (std::size_t i = 0; i < sz; ++i)
+      str04 += (bit.test(i) ? C('1') : C('0'));
+    std::reverse(str04.begin(), str04.end());
+    VERIFY( str04 == str );
+  }
+  catch(std::invalid_argument& fail) {
+    VERIFY( false );
+  }
+  catch(...) {
+    VERIFY( false );
+  }
+
+  // "aba0aba", zero = 'a', one = 'b', '0' appears
+  try {
+    const std::size_t n = 128;
+    std::basic_string_view<C> str05(L("aba0aba"));
+    std::bitset<n> bit(str05, 0, 100, C('a'), C('b'));
+    VERIFY( false );
+  }
+  catch(std::invalid_argument& fail) {
+    VERIFY( true );
+  }
+  catch(...) {
+    VERIFY( false );
+  }
+
+  // pos > str.size()
+  try {
+    std::basic_string_view<C> str(L("010101000011"));
+    const std::size_t n = 128;
+    const auto sz = str.size();
+    std::bitset<n> bit(str, sz + 1, 100);
+    VERIFY( false );
+  }
+  catch(std::out_of_range& fail) {
+    VERIFY( true );
+  }
+  catch(...) {
+    VERIFY( false );
+  }
+}
+
+int main()
+{
+  test01();
+  return 0;
+}
diff --git a/libstdc++-v3/testsuite/20_util/bitset/cons/string_view_wide.cc b/libstdc++-v3/testsuite/20_util/bitset/cons/string_view_wide.cc
new file mode 100644 (file)
index 0000000..b53fe74
--- /dev/null
@@ -0,0 +1,8 @@
+// C++26 [bitset.cons]
+
+// { dg-do run { target c++26 } }
+
+#define C wchar_t
+#define L(s) L ## s
+
+#include "./string_view.cc"