From 7c1006135ddeab216f376adc5f6135a22bfc0ff6 Mon Sep 17 00:00:00 2001 From: Jonathan Wakely Date: Mon, 22 Mar 2021 17:11:21 +0000 Subject: [PATCH] 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. --- libstdc++-v3/include/std/string_view | 33 +++- .../cons/char/range_c++20.cc | 170 ++++++++++++++++++ .../cons/wchar_t/range_c++20.cc | 170 ++++++++++++++++++ 3 files changed, 371 insertions(+), 2 deletions(-) create mode 100644 libstdc++-v3/testsuite/21_strings/basic_string_view/cons/char/range_c++20.cc create mode 100644 libstdc++-v3/testsuite/21_strings/basic_string_view/cons/wchar_t/range_c++20.cc 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 #include +#if __cplusplus > 202002L +# include +#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 _End> requires same_as, _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> + requires (!is_same_v<_DRange, basic_string_view>) + && ranges::contiguous_range<_Range> + && ranges::sized_range<_Range> + && is_same_v, _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) + 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 _End> basic_string_view(_It, _End) -> basic_string_view>; + +#if __cplusplus > 202002L + template + basic_string_view(_Range&&) + -> basic_string_view>; +#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 +// . + +// { dg-options "-std=gnu++23" } +// { dg-do run { target c++23 } } + +#include +#include + +void +test01() +{ + struct R + { + const char* begin() const + { return str; } + + const char* end() const + { return str + std::char_traits::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 ); + static_assert( std::ranges::sized_range ); + R2 r2; + std::string_view s2 = r2; // uses conversion to string_view + VERIFY( s2 == "Out of range" ); + VERIFY( std::string_view(const_cast(r2)) == s2 ); + + struct R3 : R + { + using R::begin; + using R::end; + operator const char*() { return "Orange"; } + }; + static_assert( std::ranges::contiguous_range ); + static_assert( std::ranges::sized_range ); + R3 r3; + std::string_view s3(r3); // uses conversion to const char* + VERIFY( s3 == "Orange" ); + s3 = std::string_view(const_cast(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 ); + static_assert( std::ranges::sized_range ); + 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 ); + + struct R5 : R + { + using R::begin; + using R::end; + operator std::string_view() && { return "Stranger"; } + }; + static_assert( std::ranges::contiguous_range ); + static_assert( std::ranges::sized_range ); + 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; + // range_value_t is not the right type + static_assert( ! std::is_constructible_v ); + + using V2 = std::basic_string_view>; + // V2::traits_type is not the right type + static_assert( ! std::is_constructible_v ); + + struct V3 : V2 + { + private: + using V2::traits_type; + }; + // V3::traits_type is not a valid (accessible) type + static_assert( std::is_constructible_v ); + + struct V4 : V2 + { + using traits_type = std::string_view::traits_type; + }; + // V4::traits_type is the right type + static_assert( std::is_constructible_v ); +} + +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())) ); + static_assert( noexcept(std::string_view(std::declval())) ); +} + +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 ); +} + +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 +// . + +// { dg-options "-std=gnu++23" } +// { dg-do run { target c++23 } } + +#include +#include + +void +test01() +{ + struct R + { + const wchar_t* begin() const + { return str; } + + const wchar_t* end() const + { return str + std::char_traits::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 ); + static_assert( std::ranges::sized_range ); + R2 r2; + std::wstring_view s2 = r2; // uses conversion to wstring_view + VERIFY( s2 == L"Out of range" ); + VERIFY( std::wstring_view(const_cast(r2)) == s2 ); + + struct R3 : R + { + using R::begin; + using R::end; + operator const wchar_t*() { return L"Orange"; } + }; + static_assert( std::ranges::contiguous_range ); + static_assert( std::ranges::sized_range ); + R3 r3; + std::wstring_view s3(r3); // uses conversion to const wchar_t* + VERIFY( s3 == L"Orange" ); + s3 = std::wstring_view(const_cast(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 ); + static_assert( std::ranges::sized_range ); + 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 ); + + struct R5 : R + { + using R::begin; + using R::end; + operator std::wstring_view() && { return L"Stranger"; } + }; + static_assert( std::ranges::contiguous_range ); + static_assert( std::ranges::sized_range ); + 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; + // range_value_t is not the right type + static_assert( ! std::is_constructible_v ); + + using V2 = std::basic_string_view>; + // V2::traits_type is not the right type + static_assert( ! std::is_constructible_v ); + + struct V3 : V2 + { + private: + using V2::traits_type; + }; + // V3::traits_type is not a valid (accessible) type + static_assert( std::is_constructible_v ); + + struct V4 : V2 + { + using traits_type = std::wstring_view::traits_type; + }; + // V4::traits_type is the right type + static_assert( std::is_constructible_v ); +} + +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())) ); + static_assert( noexcept(std::wstring_view(std::declval())) ); +} + +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 ); +} + +int main() +{ + test01(); + test02(); + test03(); + test04(); +}