[PATCH v2] libstdc++: construct bitset from string_view (P2697) [PR119742]
Jonathan Wakely
jwakely.gcc@gmail.com
Thu Jul 3 06:43:08 GMT 2025
On Thu, 3 Jul 2025, 00:56 Nathan Myers, <ncm@cantrip.org> wrote:
> Changes in V2:
> * Generalize private member _M_check_initial_position for use with
> both string and string_view arguments.
> * Remove unnecessary #if guards for version and hostedness.
> * Remove redundant "std::" qualifications in new code.
> * Improve Doxygen source readability.
> * Clarify commit message text.
> * Fix ChangeLog style.
>
OK for trunk, thanks
> 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 | 8 ++
> libstdc++-v3/include/bits/version.h | 10 ++
> libstdc++-v3/include/std/bitset | 82 +++++++----
> .../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, 215 insertions(+), 31 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 f4ba501c403..b89b287e8e8 100644
> --- a/libstdc++-v3/include/bits/version.def
> +++ b/libstdc++-v3/include/bits/version.def
> @@ -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";
> diff --git a/libstdc++-v3/include/bits/version.h
> b/libstdc++-v3/include/bits/version.h
> index dc8ac07be16..a70a7ede68c 100644
> --- a/libstdc++-v3/include/bits/version.h
> +++ b/libstdc++-v3/include/bits/version.h
> @@ -2273,4 +2273,14 @@
> #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
> diff --git a/libstdc++-v3/include/std/bitset
> b/libstdc++-v3/include/std/bitset
> index 8b5d270c2a9..1c1e1670c33 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 + \
> @@ -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)
> 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);
> }
> 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.50.0
>
>
-------------- next part --------------
An HTML attachment was scrubbed...
URL: <https://gcc.gnu.org/pipermail/libstdc++/attachments/20250703/eb4c8d4f/attachment-0001.htm>
More information about the Libstdc++
mailing list