On Tue, 1 Jul 2025 at 23:36, Nathan Myers <n...@cantrip.org> wrote:
>
> Add a bitset constructor from string_view, with other arguments
> matching the constructor from string. Test in ways that exercise
> code paths not checked in existing tests for other constructors.
> Fix existing tests that would fail to detect incorrect exception
> behavior.
>
> libstdc++-v3/ChangeLog:
>         PR libstdc++/119742
>         * include/bits/version.def: new preprocessor symbol
>         * include/bits/version.h: new preprocessor symbol
>         * include/std/bitset: new 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: new tests
>         * testsuite/20_util/bitset/cons/string_view_wide.cc: new tests

Full sentences in these ChangeLog lines please, as with the explicit
ctors patch.

> ---
>  libstdc++-v3/include/bits/version.def         |   8 ++
>  libstdc++-v3/include/bits/version.h           |  10 ++
>  libstdc++-v3/include/std/bitset               |  49 +++++++
>  .../testsuite/20_util/bitset/cons/1.cc        |   1 +
>  .../testsuite/20_util/bitset/cons/6282.cc     |   5 +-
>  .../20_util/bitset/cons/string_view.cc        | 132 ++++++++++++++++++
>  .../20_util/bitset/cons/string_view_wide.cc   |   8 ++
>  7 files changed, 210 insertions(+), 3 deletions(-)
>  create mode 100644 libstdc++-v3/testsuite/20_util/bitset/cons/string_view.cc
>  create mode 100644 
> libstdc++-v3/testsuite/20_util/bitset/cons/string_view_wide.cc
>
> diff --git a/libstdc++-v3/include/bits/version.def 
> b/libstdc++-v3/include/bits/version.def
> index 880586e9126..d82585e1238 100644
> --- a/libstdc++-v3/include/bits/version.def
> +++ b/libstdc++-v3/include/bits/version.def
> @@ -2012,6 +2012,14 @@ ftms = {
>    };
>  };
>
> +ftms = {
> +  name = bitset  // ...construct from string_view

This comment is useful, thanks.

> +  values = {
> +    v = 202306;
> +    cxxmin = 26;

I wondered whether we wanted to use hosted=yes here, because
<string_view> isn't required to be freestanding, but we do define it
unconditionally (and it seems useful to have in a freestanding env).
So not using hosted=yes here is correct.

> +  };
> +};
> +
>  // Standard test specifications.
>  stds[97] = ">= 199711L";
>  stds[03] = ">= 199711L";
> diff --git a/libstdc++-v3/include/bits/version.h 
> b/libstdc++-v3/include/bits/version.h
> index 4300adb2276..c0d3cbe623f 100644
> --- a/libstdc++-v3/include/bits/version.h
> +++ b/libstdc++-v3/include/bits/version.h
> @@ -2253,4 +2253,14 @@
>  #endif /* !defined(__cpp_lib_sstream_from_string_view) && 
> defined(__glibcxx_want_sstream_from_string_view) */
>  #undef __glibcxx_want_sstream_from_string_view
>
> +#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
> diff --git a/libstdc++-v3/include/std/bitset b/libstdc++-v3/include/std/bitset
> index 8b5d270c2a9..f4947902384 100644
> --- a/libstdc++-v3/include/std/bitset
> +++ b/libstdc++-v3/include/std/bitset
> @@ -61,8 +61,13 @@
>  #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 + \
> @@ -831,6 +836,24 @@ _GLIBCXX_BEGIN_NAMESPACE_CONTAINER
>        }
>  #endif // HOSTED
>
> +#ifdef __cpp_lib_bitset
> +      template<class _CharT, class _Traits>

I was going to say that this should use 'typename' not 'class' but I
see that <bitset> is consistently "wrong" and uses 'class' everywhere.
Let's not change that now.

> +      constexpr void
> +      _M_check_initial_position(
> +       std::basic_string_view<_CharT, _Traits> __s,
> +       typename std::basic_string_view<_CharT, _Traits>::size_type __position

We don't need the std:: qualification on these names. That's also
consistently wrong in this file though. I don't mind if you change it
now for the new constructors, or keep the std:: here and we can fix it
throughout the file in a later commit.

> +      ) const
> +      {
> +# if _GLIBCXX_HOSTED

We define __throw_out_of_range_fmt for non-hosted, it just calls
terminate() (see include/bits/functexcept.h) so I think we can do this
unconditionally. Otherwise, we wouldn't have these range checks at all
for non-hosted. (Looks as though we should remove a similar HOSTED
check in the bitset(const charT*, ...) constructor).


> +       if (__position > __s.size())
> +         __throw_out_of_range_fmt(__N("bitset::bitset: __position "
> +                                      "(which is %zu) > __s.size() "
> +                                      "(which is %zu)"),
> +                                  __position, __s.size());
> +# endif
> +      }
> +#endif

Do we need this function at all though?
Could we change the existing _M_check_initial_position to be a
function template?

#if _GLIBCXX_HOSTED || __cpp_lib_bitset
      template<class _Str>
      _GLIBCXX23_CONSTEXPR
      void
      _M_check_initial_position(const _Str& __s,
                size_t __position) const

That will work for strings and string views.

> +
>        _GLIBCXX23_CONSTEXPR
>        void _M_check(size_t __position, const char *__s) const
>        {
> @@ -1008,6 +1031,32 @@ _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 @a 0 and @a 1 
> characters.
> +       *  @param  __position  Index of the first character in @a __s to use.
> +       *  @param  __n    The maximum number of characters from @a __s to use.
> +       *  @throw std::out_of_range If @a __position is bigger the size
> +       *  of @a __s.
> +       *  @throw  std::invalid_argument  If a character appears in the string
> +       *                                 which is neither @a 0 nor @a 1.

We can use Markdown in our Doxygen comments these days, so in new
comments I prefer to use `__s` rather than @a __s. I find the Doxygen
commands much more distracting when reading the comments directly in
the source, rather than reading the generated HTML docs.


> +       */
> +      template<class _CharT, class _Traits>
> +       constexpr explicit
> +       bitset(std::basic_string_view<_CharT, _Traits> __s,
> +         std::basic_string_view<_CharT, _Traits>::size_type __position = 0,
> +         std::basic_string_view<_CharT, _Traits>::size_type __n =
> +           std::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.
> diff --git a/libstdc++-v3/testsuite/20_util/bitset/cons/1.cc 
> b/libstdc++-v3/testsuite/20_util/bitset/cons/1.cc
> index 0a40b6cd965..6441332c8d6 100644
> --- a/libstdc++-v3/testsuite/20_util/bitset/cons/1.cc
> +++ b/libstdc++-v3/testsuite/20_util/bitset/cons/1.cc
> @@ -47,6 +47,7 @@ void test01(void)
>    const size_t n3 = 128;
>    try {
>      std::bitset<n3> bit03(str01, 5);
> +    VERIFY(false);

Thanks for noticing this was missing!

>    }
>    catch(std::invalid_argument& fail) {
>      VERIFY( true );
> diff --git a/libstdc++-v3/testsuite/20_util/bitset/cons/6282.cc 
> b/libstdc++-v3/testsuite/20_util/bitset/cons/6282.cc
> index fafa9fc808b..ee348c35b2a 100644
> --- a/libstdc++-v3/testsuite/20_util/bitset/cons/6282.cc
> +++ b/libstdc++-v3/testsuite/20_util/bitset/cons/6282.cc
> @@ -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
> index 00000000000..ec3a6c86b68
> --- /dev/null
> +++ b/libstdc++-v3/testsuite/20_util/bitset/cons/string_view.cc
> @@ -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
> index 00000000000..b53fe74a7ce
> --- /dev/null
> +++ b/libstdc++-v3/testsuite/20_util/bitset/cons/string_view_wide.cc
> @@ -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"
> --
> 2.49.0
>

Reply via email to