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 >