This implements the new string_view constructor proposed by P1989R2. This hasn't been voted into the C++23 draft yet, but it's been reviewed by LWG and is expected to be approved at the next WG21 meeting.
libstdc++-v3/ChangeLog: * include/std/string_view (basic_string_view(Range&&)): Define new constructor and deduction guide. * testsuite/21_strings/basic_string_view/cons/char/range_c++20.cc: New test. * testsuite/21_strings/basic_string_view/cons/wchar_t/range_c++20.cc: New test. Tested powerpc64le-linux. Committed to trunk. (I wrote "C++20" in the commit log summary but it's a C++23 feature, which is why I'm confortable pushing it now in stage 4.)
commit 7c1006135ddeab216f376adc5f6135a22bfc0ff6 Author: Jonathan Wakely <jwak...@redhat.com> Date: Mon Mar 22 17:11:21 2021 libstdc++: Implement string_view range constructor for C++20 This implements the new string_view constructor proposed by P1989R2. This hasn't been voted into the C++23 draft yet, but it's been reviewed by LWG and is expected to be approved at the next WG21 meeting. libstdc++-v3/ChangeLog: * include/std/string_view (basic_string_view(Range&&)): Define new constructor and deduction guide. * testsuite/21_strings/basic_string_view/cons/char/range_c++20.cc: New test. * testsuite/21_strings/basic_string_view/cons/wchar_t/range_c++20.cc: New test. diff --git a/libstdc++-v3/include/std/string_view b/libstdc++-v3/include/std/string_view index dba757fad6b..31502f7d58d 100644 --- a/libstdc++-v3/include/std/string_view +++ b/libstdc++-v3/include/std/string_view @@ -45,6 +45,10 @@ #include <bits/ostream_insert.h> #include <ext/numeric_traits.h> +#if __cplusplus > 202002L +# include <bits/ranges_base.h> +#endif + namespace std _GLIBCXX_VISIBILITY(default) { _GLIBCXX_BEGIN_NAMESPACE_VERSION @@ -135,7 +139,7 @@ _GLIBCXX_BEGIN_NAMESPACE_VERSION : _M_len{__len}, _M_str{__str} { } -#if __cplusplus > 201703L && __cpp_lib_concepts +#if __cplusplus >= 202002L && __cpp_lib_concepts template<contiguous_iterator _It, sized_sentinel_for<_It> _End> requires same_as<iter_value_t<_It>, _CharT> && (!convertible_to<_End, size_type>) @@ -143,7 +147,26 @@ _GLIBCXX_BEGIN_NAMESPACE_VERSION basic_string_view(_It __first, _End __last) : _M_len(__last - __first), _M_str(std::to_address(__first)) { } -#endif + +#if __cplusplus > 202002L + template<typename _Range, typename _DRange = remove_cvref_t<_Range>> + requires (!is_same_v<_DRange, basic_string_view>) + && ranges::contiguous_range<_Range> + && ranges::sized_range<_Range> + && is_same_v<ranges::range_value_t<_Range>, _CharT> + && (!is_convertible_v<_Range, const _CharT*>) + && (!requires (_DRange& __d) { + __d.operator ::std::basic_string_view<_CharT, _Traits>(); + }) + && (!requires { typename _DRange::traits_type; } + || is_same_v<typename _DRange::traits_type, _Traits>) + constexpr + basic_string_view(_Range&& __r) + noexcept(noexcept(ranges::size(__r)) && noexcept(ranges::data(__r))) + : _M_len(ranges::size(__r)), _M_str(ranges::data(__r)) + { } +#endif // C++23 +#endif // C++20 constexpr basic_string_view& operator=(const basic_string_view&) noexcept = default; @@ -490,6 +513,12 @@ _GLIBCXX_BEGIN_NAMESPACE_VERSION #if __cplusplus > 201703L && __cpp_lib_concepts && __cpp_deduction_guides template<contiguous_iterator _It, sized_sentinel_for<_It> _End> basic_string_view(_It, _End) -> basic_string_view<iter_value_t<_It>>; + +#if __cplusplus > 202002L + template<ranges::contiguous_range _Range> + basic_string_view(_Range&&) + -> basic_string_view<ranges::range_value_t<_Range>>; +#endif #endif // [string.view.comparison], non-member basic_string_view comparison function diff --git a/libstdc++-v3/testsuite/21_strings/basic_string_view/cons/char/range_c++20.cc b/libstdc++-v3/testsuite/21_strings/basic_string_view/cons/char/range_c++20.cc new file mode 100644 index 00000000000..fa85f1994c9 --- /dev/null +++ b/libstdc++-v3/testsuite/21_strings/basic_string_view/cons/char/range_c++20.cc @@ -0,0 +1,170 @@ +// Copyright (C) 2021 Free Software Foundation, Inc. +// +// 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. + +// You should have received a copy of the GNU General Public License along +// with this library; see the file COPYING3. If not see +// <http://www.gnu.org/licenses/>. + +// { dg-options "-std=gnu++23" } +// { dg-do run { target c++23 } } + +#include <string_view> +#include <testsuite_hooks.h> + +void +test01() +{ + struct R + { + const char* begin() const + { return str; } + + const char* end() const + { return str + std::char_traits<char>::length(str); } + + const char* str = "Home on the range"; + }; + + R r; + std::string_view s = r; + VERIFY( s == r.str ); + VERIFY( s.data() == std::ranges::data(r) ); + VERIFY( s.size() == std::ranges::size(r) ); + + struct R2 : R + { + using R::begin; + using R::end; + operator std::string_view() const { return "Out of range"; } + }; + static_assert( std::ranges::contiguous_range<R2> ); + static_assert( std::ranges::sized_range<R2> ); + R2 r2; + std::string_view s2 = r2; // uses conversion to string_view + VERIFY( s2 == "Out of range" ); + VERIFY( std::string_view(const_cast<const R2&>(r2)) == s2 ); + + struct R3 : R + { + using R::begin; + using R::end; + operator const char*() { return "Orange"; } + }; + static_assert( std::ranges::contiguous_range<R3> ); + static_assert( std::ranges::sized_range<R3> ); + R3 r3; + std::string_view s3(r3); // uses conversion to const char* + VERIFY( s3 == "Orange" ); + s3 = std::string_view(const_cast<const R3&>(r3)); // uses range constructor + VERIFY( s3 == "Home on the range" ); + + struct R4 : R + { + using R::begin; + using R::end; + operator std::string_view() { return "Strange"; } + }; + static_assert( std::ranges::contiguous_range<R4> ); + static_assert( std::ranges::sized_range<R4> ); + R4 r4; + std::string_view s4 = r4; // Uses conversion to string_view + VERIFY( s4 == "Strange" ); + // Cannot construct from const R4 because of non-const conversion op: + static_assert( ! std::is_constructible_v<std::string_view, const R4&> ); + + struct R5 : R + { + using R::begin; + using R::end; + operator std::string_view() && { return "Stranger"; } + }; + static_assert( std::ranges::contiguous_range<R5> ); + static_assert( std::ranges::sized_range<R5> ); + R5 r5; + std::string_view s5 = r5; // Uses range constructor + VERIFY( s5 == r5.str ); + s5 = std::string_view(std::move(r5)); // In C++20 this used conversion op. + VERIFY( s5 == r5.str ); // In C++23 it uses range constructor. + + char arr[] = "arrangement\0with\0nulls"; + std::string_view sa = arr; // Does not use range constructor + VERIFY( sa.data() == arr ); + VERIFY( sa == "arrangement" ); + VERIFY( std::end(sa) != std::end(arr) ); +} + +void +test02() +{ + using V1 = std::basic_string_view<signed char>; + // range_value_t<V1> is not the right type + static_assert( ! std::is_constructible_v<std::string_view, V1> ); + + using V2 = std::basic_string_view<char, __gnu_cxx::char_traits<char>>; + // V2::traits_type is not the right type + static_assert( ! std::is_constructible_v<std::string_view, V2> ); + + struct V3 : V2 + { + private: + using V2::traits_type; + }; + // V3::traits_type is not a valid (accessible) type + static_assert( std::is_constructible_v<std::string_view, V3> ); + + struct V4 : V2 + { + using traits_type = std::string_view::traits_type; + }; + // V4::traits_type is the right type + static_assert( std::is_constructible_v<std::string_view, V4> ); +} + +void +test03() +{ + struct R + { + char* begin() { return nullptr; } + const char* begin() const noexcept { return nullptr; } + + char* end() { return nullptr; } + const char* end() const noexcept { return nullptr; } + }; + + static_assert( ! noexcept(std::string_view(std::declval<R&>())) ); + static_assert( noexcept(std::string_view(std::declval<const R&>())) ); +} + +void +test04() +{ + struct R + { + const char* begin() const { return nullptr; } + const char* end() const { return nullptr; } + }; + + R r; + std::basic_string_view s = r; // Use deduction guide. + + static_assert( std::is_same_v<decltype(s), std::string_view> ); +} + +int main() +{ + test01(); + test02(); + test03(); + test04(); +} diff --git a/libstdc++-v3/testsuite/21_strings/basic_string_view/cons/wchar_t/range_c++20.cc b/libstdc++-v3/testsuite/21_strings/basic_string_view/cons/wchar_t/range_c++20.cc new file mode 100644 index 00000000000..cf73ae36a60 --- /dev/null +++ b/libstdc++-v3/testsuite/21_strings/basic_string_view/cons/wchar_t/range_c++20.cc @@ -0,0 +1,170 @@ +// Copyright (C) 2021 Free Software Foundation, Inc. +// +// 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. + +// You should have received a copy of the GNU General Public License along +// with this library; see the file COPYING3. If not see +// <http://www.gnu.org/licenses/>. + +// { dg-options "-std=gnu++23" } +// { dg-do run { target c++23 } } + +#include <string_view> +#include <testsuite_hooks.h> + +void +test01() +{ + struct R + { + const wchar_t* begin() const + { return str; } + + const wchar_t* end() const + { return str + std::char_traits<wchar_t>::length(str); } + + const wchar_t* str = L"Home on the range"; + }; + + R r; + std::wstring_view s = r; + VERIFY( s == r.str ); + VERIFY( s.data() == std::ranges::data(r) ); + VERIFY( s.size() == std::ranges::size(r) ); + + struct R2 : R + { + using R::begin; + using R::end; + operator std::wstring_view() const { return L"Out of range"; } + }; + static_assert( std::ranges::contiguous_range<R2> ); + static_assert( std::ranges::sized_range<R2> ); + R2 r2; + std::wstring_view s2 = r2; // uses conversion to wstring_view + VERIFY( s2 == L"Out of range" ); + VERIFY( std::wstring_view(const_cast<const R2&>(r2)) == s2 ); + + struct R3 : R + { + using R::begin; + using R::end; + operator const wchar_t*() { return L"Orange"; } + }; + static_assert( std::ranges::contiguous_range<R3> ); + static_assert( std::ranges::sized_range<R3> ); + R3 r3; + std::wstring_view s3(r3); // uses conversion to const wchar_t* + VERIFY( s3 == L"Orange" ); + s3 = std::wstring_view(const_cast<const R3&>(r3)); // uses range constructor + VERIFY( s3 == L"Home on the range" ); + + struct R4 : R + { + using R::begin; + using R::end; + operator std::wstring_view() { return L"Strange"; } + }; + static_assert( std::ranges::contiguous_range<R4> ); + static_assert( std::ranges::sized_range<R4> ); + R4 r4; + std::wstring_view s4 = r4; // Uses conversion to wstring_view + VERIFY( s4 == L"Strange" ); + // Cannot construct from const R4 because of non-const conversion op: + static_assert( ! std::is_constructible_v<std::wstring_view, const R4&> ); + + struct R5 : R + { + using R::begin; + using R::end; + operator std::wstring_view() && { return L"Stranger"; } + }; + static_assert( std::ranges::contiguous_range<R5> ); + static_assert( std::ranges::sized_range<R5> ); + R5 r5; + std::wstring_view s5 = r5; // Uses range constructor + VERIFY( s5 == r5.str ); + s5 = std::wstring_view(std::move(r5)); // In C++20 this used conversion op. + VERIFY( s5 == r5.str ); // In C++23 it uses range constructor. + + wchar_t arr[] = L"arrangement\0with\0nulls"; + std::wstring_view sa = arr; // Does not use range constructor + VERIFY( sa.data() == arr ); + VERIFY( sa == L"arrangement" ); + VERIFY( std::end(sa) != std::end(arr) ); +} + +void +test02() +{ + using V1 = std::basic_string_view<char>; + // range_value_t<V1> is not the right type + static_assert( ! std::is_constructible_v<std::wstring_view, V1> ); + + using V2 = std::basic_string_view<wchar_t, __gnu_cxx::char_traits<wchar_t>>; + // V2::traits_type is not the right type + static_assert( ! std::is_constructible_v<std::wstring_view, V2> ); + + struct V3 : V2 + { + private: + using V2::traits_type; + }; + // V3::traits_type is not a valid (accessible) type + static_assert( std::is_constructible_v<std::wstring_view, V3> ); + + struct V4 : V2 + { + using traits_type = std::wstring_view::traits_type; + }; + // V4::traits_type is the right type + static_assert( std::is_constructible_v<std::wstring_view, V4> ); +} + +void +test03() +{ + struct R + { + wchar_t* begin() { return nullptr; } + const wchar_t* begin() const noexcept { return nullptr; } + + wchar_t* end() { return nullptr; } + const wchar_t* end() const noexcept { return nullptr; } + }; + + static_assert( ! noexcept(std::wstring_view(std::declval<R&>())) ); + static_assert( noexcept(std::wstring_view(std::declval<const R&>())) ); +} + +void +test04() +{ + struct R + { + const wchar_t* begin() const { return nullptr; } + const wchar_t* end() const { return nullptr; } + }; + + R r; + std::basic_string_view s = r; // Use deduction guide. + + static_assert( std::is_same_v<decltype(s), std::wstring_view> ); +} + +int main() +{ + test01(); + test02(); + test03(); + test04(); +}