* [PATCH 1/5] libstdc++: Implement P2325 changes to default-constructibilty of views
@ 2021-06-17 15:22 Patrick Palka
2021-06-17 15:22 ` [PATCH 2/5] libstdc++: Move ranges algos used by <ranges> into ranges_util.h Patrick Palka
` (4 more replies)
0 siblings, 5 replies; 11+ messages in thread
From: Patrick Palka @ 2021-06-17 15:22 UTC (permalink / raw)
To: gcc-patches; +Cc: libstdc++, Patrick Palka
This implements the wording changes of P2325R3 "Views should not be
required to be default constructible". Changes are relatively
straightforward, besides perhaps those to __box (which now stands
for copyable-box instead of semiregular-box) and __non_propagating_cache.
For __box, this patch implements the recommended practice to also avoid
std::optional when the boxed type is nothrow_move/copy_constructible.
For __non_propagating_cache, now that it's used by split_view::_M_current,
we need to add assignment from a value of the underlying type to the
subset of the std::optional API implemented for the cache (needed by
split_view::begin()). Hence the new __non_propagating_cache::operator=
overload.
While we're changing __box, this fixes the undesirable list-init in
the constuctors of the partial specialization as reported in PR100475
comment #7.
libstdc++-v3/ChangeLog:
* include/bits/iterator_concepts.h (weakly_incrementable): Remove
default_initializable requirement.
* include/bits/ranges_base.h (ranges::view): Likewise.
* include/bits/ranges_util.h (subrange): Constrain the default
ctor.
* include/bits/stl_iterator.h (back_insert_iterator): Remove the
default ctor.
(front_insert_iterator): Likewise.
(insert_iterator): Likewise. Remove NSDMIs.
(common_iterator): Constrain the default ctor.
(counted_iterator): Likewise.
* include/bits/stream_iterator.h (ostream_iterator): Remove the
default ctor.
* include/std/ranges (__detail::__box::operator=): Handle
self-assignment in the primary template.
(__detail::__box): In the partial specialization: adjust
constraints as per P2325. Add specialized operator= for the
case when the wrapped type is not copyable. Constrain the
default ctor. Avoid list-initialization.
(single_view): Constraint the default ctor.
(iota_view): Relax semiregular constraint to copyable.
Constrain the default ctor.
(iota_view::_Iterator): Constraint the default ctor.
(basic_istream_view): Remove the default ctor. Remove NSDMIs.
Remove redundant checks for empty _M_stream.
(basic_istream_view::_Iterator): Likewise.
(ref_view): Remove the default ctor. Remove NSDMIs.
(ref_view::_Iterator): Constrain the default ctor.
(__detail::__non_propagating_cache::operator=): Define overload
for assigning from a value of the underlying type.
(filter_view): Likewise.
(filter_view::_Iterator): Likewise.
(transform_view): Likewise.
(transform_view::_Iterator): Likewise.
(take_view): Likewise.
(take_view::_Iterator): Likewise.
(take_while_view): Likewise.
(take_while_view::_Iterator): Likewise.
(drop_while_view): Likewise.
(drop_while_view::_Iterator): Likewise.
(join_view): Likewise.
(split_view::_OuterIter::__current): Adjust after changing the
type of _M_current.
(split_view::_M_current): Wrap it in a __non_propagating_cache.
(split_view::split_view): Constrain the default ctor.
(common_view): Constrain the default ctor.
(reverse_view): Likewise.
(elements_view): Likewise.
* include/std/span (enable_view<span<_ElementType, _Extent>>):
Define this partial specialization to true unconditionally.
* include/std/version (__cpp_lib_ranges): Adjust value.
* testsuite/24_iterators/back_insert_iterator/constexpr.cc:
Don't attempt to default construct a back_insert_iterator.
* testsuite/24_iterators/front_insert_iterator/constexpr.cc:
Don't attempt to default construct a front_insert_iterator.
* testsuite/24_iterators/insert_iterator/constexpr.cc:
Don't attempt to default construct an insert_iterator.
* testsuite/24_iterators/ostream_iterator/requirements/constexpr.cc:
Remove this test for default constructibility of ostream_iterator.
* testsuite/std/ranges/97600.cc: Don't attempt to default
construct a basic_istream_view.
* testsuite/std/ranges/adaptors/detail/semiregular_box.cc:
Rename to ...
* testsuite/std/ranges/adaptors/detail/copyable_box.cc: ... this.
(test02): Adjust now that __box is copyable-box not
semiregular-box.
(test03): New test.
* testsuite/std/ranges/p2325.cc: New test.
* testsuite/std/ranges/single_view.cc (test06): New test.
* testsuite/std/ranges/view.cc: Adjust now that view doesn't
require default_initializable.
---
libstdc++-v3/include/bits/iterator_concepts.h | 3 +-
libstdc++-v3/include/bits/ranges_base.h | 3 +-
libstdc++-v3/include/bits/ranges_util.h | 2 +-
libstdc++-v3/include/bits/stl_iterator.h | 16 +-
libstdc++-v3/include/bits/stream_iterator.h | 5 -
libstdc++-v3/include/std/ranges | 160 ++++++++++++------
libstdc++-v3/include/std/span | 3 +-
libstdc++-v3/include/std/version | 2 +-
.../back_insert_iterator/constexpr.cc | 3 +-
.../front_insert_iterator/constexpr.cc | 3 +-
.../24_iterators/insert_iterator/constexpr.cc | 3 +-
.../requirements/constexpr.cc | 24 ---
libstdc++-v3/testsuite/std/ranges/97600.cc | 3 +-
.../{semiregular_box.cc => copyable_box.cc} | 51 +++++-
libstdc++-v3/testsuite/std/ranges/p2325.cc | 155 +++++++++++++++++
.../testsuite/std/ranges/single_view.cc | 15 ++
libstdc++-v3/testsuite/std/ranges/view.cc | 2 +-
17 files changed, 335 insertions(+), 118 deletions(-)
delete mode 100644 libstdc++-v3/testsuite/24_iterators/ostream_iterator/requirements/constexpr.cc
rename libstdc++-v3/testsuite/std/ranges/adaptors/detail/{semiregular_box.cc => copyable_box.cc} (70%)
create mode 100644 libstdc++-v3/testsuite/std/ranges/p2325.cc
diff --git a/libstdc++-v3/include/bits/iterator_concepts.h b/libstdc++-v3/include/bits/iterator_concepts.h
index 11748e5ed7b..c273056c204 100644
--- a/libstdc++-v3/include/bits/iterator_concepts.h
+++ b/libstdc++-v3/include/bits/iterator_concepts.h
@@ -594,8 +594,7 @@ _GLIBCXX_BEGIN_NAMESPACE_VERSION
/// Requirements on types that can be incremented with ++.
template<typename _Iter>
- concept weakly_incrementable = default_initializable<_Iter>
- && movable<_Iter>
+ concept weakly_incrementable = movable<_Iter>
&& requires(_Iter __i)
{
typename iter_difference_t<_Iter>;
diff --git a/libstdc++-v3/include/bits/ranges_base.h b/libstdc++-v3/include/bits/ranges_base.h
index 25af4b742a6..9d749c8d9b7 100644
--- a/libstdc++-v3/include/bits/ranges_base.h
+++ b/libstdc++-v3/include/bits/ranges_base.h
@@ -619,8 +619,7 @@ namespace ranges
/// [range.view] The ranges::view concept.
template<typename _Tp>
concept view
- = range<_Tp> && movable<_Tp> && default_initializable<_Tp>
- && enable_view<_Tp>;
+ = range<_Tp> && movable<_Tp> && enable_view<_Tp>;
// [range.refinements]
diff --git a/libstdc++-v3/include/bits/ranges_util.h b/libstdc++-v3/include/bits/ranges_util.h
index dd829ed957f..d7b12b3d985 100644
--- a/libstdc++-v3/include/bits/ranges_util.h
+++ b/libstdc++-v3/include/bits/ranges_util.h
@@ -241,7 +241,7 @@ namespace ranges
[[no_unique_address]] _Size<__size_type> _M_size = {};
public:
- subrange() = default;
+ subrange() requires default_initializable<_It> = default;
constexpr
subrange(__detail::__convertible_to_non_slicing<_It> auto __i, _Sent __s)
diff --git a/libstdc++-v3/include/bits/stl_iterator.h b/libstdc++-v3/include/bits/stl_iterator.h
index 8768624b7d1..6ec046b597b 100644
--- a/libstdc++-v3/include/bits/stl_iterator.h
+++ b/libstdc++-v3/include/bits/stl_iterator.h
@@ -639,8 +639,6 @@ _GLIBCXX_BEGIN_NAMESPACE_VERSION
typedef _Container container_type;
#if __cplusplus > 201703L
using difference_type = ptrdiff_t;
-
- constexpr back_insert_iterator() noexcept : container(nullptr) { }
#endif
/// The only way to create this %iterator is with a container.
@@ -742,8 +740,6 @@ _GLIBCXX_BEGIN_NAMESPACE_VERSION
typedef _Container container_type;
#if __cplusplus > 201703L
using difference_type = ptrdiff_t;
-
- constexpr front_insert_iterator() noexcept : container(nullptr) { }
#endif
/// The only way to create this %iterator is with a container.
@@ -843,17 +839,12 @@ _GLIBCXX_BEGIN_NAMESPACE_VERSION
{
#if __cplusplus > 201703L && defined __cpp_lib_concepts
using _Iter = std::__detail::__range_iter_t<_Container>;
-
- protected:
- _Container* container = nullptr;
- _Iter iter = _Iter();
#else
typedef typename _Container::iterator _Iter;
-
+#endif
protected:
_Container* container;
_Iter iter;
-#endif
public:
/// A nested typedef for the type of whatever container you used.
@@ -861,8 +852,6 @@ _GLIBCXX_BEGIN_NAMESPACE_VERSION
#if __cplusplus > 201703L && defined __cpp_lib_concepts
using difference_type = ptrdiff_t;
-
- insert_iterator() = default;
#endif
/**
@@ -1740,6 +1729,7 @@ _GLIBCXX_BEGIN_NAMESPACE_VERSION
constexpr
common_iterator()
noexcept(is_nothrow_default_constructible_v<_It>)
+ requires default_initializable<_It>
: _M_it(), _M_index(0)
{ }
@@ -2117,7 +2107,7 @@ _GLIBCXX_BEGIN_NAMESPACE_VERSION
// iterator_concept defined in __counted_iter_concept
// iterator_category defined in __counted_iter_cat
- constexpr counted_iterator() = default;
+ constexpr counted_iterator() requires default_initializable<_It> = default;
constexpr
counted_iterator(_It __i, iter_difference_t<_It> __n)
diff --git a/libstdc++-v3/include/bits/stream_iterator.h b/libstdc++-v3/include/bits/stream_iterator.h
index fd8920b8d01..d07474d4996 100644
--- a/libstdc++-v3/include/bits/stream_iterator.h
+++ b/libstdc++-v3/include/bits/stream_iterator.h
@@ -192,11 +192,6 @@ _GLIBCXX_BEGIN_NAMESPACE_VERSION
const _CharT* _M_string;
public:
-#if __cplusplus > 201703L
- constexpr ostream_iterator() noexcept
- : _M_stream(nullptr), _M_string(nullptr) { }
-#endif
-
/// Construct from an ostream.
ostream_iterator(ostream_type& __s)
: _M_stream(std::__addressof(__s)), _M_string(0) {}
diff --git a/libstdc++-v3/include/std/ranges b/libstdc++-v3/include/std/ranges
index 73c35dafd29..f96adf63d10 100644
--- a/libstdc++-v3/include/std/ranges
+++ b/libstdc++-v3/include/std/ranges
@@ -113,10 +113,13 @@ namespace ranges
noexcept(is_nothrow_copy_constructible_v<_Tp>)
requires (!copyable<_Tp>)
{
- if ((bool)__that)
- this->emplace(*__that);
- else
- this->reset();
+ if (this != std::__addressof(__that))
+ {
+ if ((bool)__that)
+ this->emplace(*__that);
+ else
+ this->reset();
+ }
return *this;
}
@@ -125,37 +128,42 @@ namespace ranges
noexcept(is_nothrow_move_constructible_v<_Tp>)
requires (!movable<_Tp>)
{
- if ((bool)__that)
- this->emplace(std::move(*__that));
- else
- this->reset();
+ if (this != std::__addressof(__that))
+ {
+ if ((bool)__that)
+ this->emplace(std::move(*__that));
+ else
+ this->reset();
+ }
return *this;
}
};
- // For types which are already semiregular, this specialization of the
- // semiregular wrapper stores the object directly without going through
+ // For types which are already copyable, this specialization of the
+ // copyable wrapper stores the object directly without going through
// std::optional. It provides just the subset of the primary template's
// API that we currently use.
- template<__boxable _Tp> requires semiregular<_Tp>
+ template<__boxable _Tp>
+ requires copyable<_Tp> || (is_nothrow_move_constructible_v<_Tp>
+ && is_nothrow_copy_constructible_v<_Tp>)
struct __box<_Tp>
{
private:
[[no_unique_address]] _Tp _M_value = _Tp();
public:
- __box() = default;
+ __box() requires default_initializable<_Tp> = default;
constexpr explicit
__box(const _Tp& __t)
noexcept(is_nothrow_copy_constructible_v<_Tp>)
- : _M_value{__t}
+ : _M_value(__t)
{ }
constexpr explicit
__box(_Tp&& __t)
noexcept(is_nothrow_move_constructible_v<_Tp>)
- : _M_value{std::move(__t)}
+ : _M_value(std::move(__t))
{ }
template<typename... _Args>
@@ -166,6 +174,38 @@ namespace ranges
: _M_value(std::forward<_Args>(__args)...)
{ }
+ __box(const __box&) = default;
+ __box(__box&&) = default;
+ __box& operator=(const __box&) requires copyable<_Tp> = default;
+ __box& operator=(__box&&) requires copyable<_Tp> = default;
+
+ // When _Tp is nothrow_copy_constructible but not copy_assignable,
+ // copy assignment is implemented via destroy-then-copy-construct.
+ constexpr __box&
+ operator=(const __box& __that) noexcept
+ {
+ static_assert(is_nothrow_copy_constructible_v<_Tp>);
+ if (this != std::__addressof(__that))
+ {
+ _M_value.~_Tp();
+ std::construct_at(std::__addressof(_M_value), *__that);
+ }
+ return *this;
+ }
+
+ // Likewise for move assignment.
+ constexpr __box&
+ operator=(__box&& __that) noexcept
+ {
+ static_assert(is_nothrow_move_constructible_v<_Tp>);
+ if (this != std::__addressof(__that))
+ {
+ _M_value.~_Tp();
+ std::construct_at(std::__addressof(_M_value), std::move(*__that));
+ }
+ return *this;
+ }
+
constexpr bool
has_value() const noexcept
{ return true; };
@@ -193,7 +233,7 @@ namespace ranges
class single_view : public view_interface<single_view<_Tp>>
{
public:
- single_view() = default;
+ single_view() requires default_initializable<_Tp> = default;
constexpr explicit
single_view(const _Tp& __t)
@@ -308,7 +348,7 @@ namespace ranges
template<weakly_incrementable _Winc,
semiregular _Bound = unreachable_sentinel_t>
requires std::__detail::__weakly_eq_cmp_with<_Winc, _Bound>
- && semiregular<_Winc>
+ && copyable<_Winc>
class iota_view : public view_interface<iota_view<_Winc, _Bound>>
{
private:
@@ -337,7 +377,7 @@ namespace ranges
using value_type = _Winc;
using difference_type = __detail::__iota_diff_t<_Winc>;
- _Iterator() = default;
+ _Iterator() requires default_initializable<_Winc> = default;
constexpr explicit
_Iterator(_Winc __value)
@@ -534,7 +574,7 @@ namespace ranges
[[no_unique_address]] _Bound _M_bound = _Bound();
public:
- iota_view() = default;
+ iota_view() requires default_initializable<_Winc> = default;
constexpr explicit
iota_view(_Winc __value)
@@ -643,8 +683,6 @@ namespace views
: public view_interface<basic_istream_view<_Val, _CharT, _Traits>>
{
public:
- basic_istream_view() = default;
-
constexpr explicit
basic_istream_view(basic_istream<_CharT, _Traits>& __stream)
: _M_stream(std::__addressof(__stream))
@@ -653,8 +691,7 @@ namespace views
constexpr auto
begin()
{
- if (_M_stream != nullptr)
- *_M_stream >> _M_object;
+ *_M_stream >> _M_object;
return _Iterator{this};
}
@@ -663,8 +700,8 @@ namespace views
{ return default_sentinel; }
private:
- basic_istream<_CharT, _Traits>* _M_stream = nullptr;
- _Val _M_object = _Val();
+ basic_istream<_CharT, _Traits>* _M_stream;
+ _Val _M_object;
struct _Iterator
{
@@ -673,8 +710,6 @@ namespace views
using difference_type = ptrdiff_t;
using value_type = _Val;
- _Iterator() = default;
-
constexpr explicit
_Iterator(basic_istream_view* __parent) noexcept
: _M_parent(__parent)
@@ -688,7 +723,6 @@ namespace views
_Iterator&
operator++()
{
- __glibcxx_assert(_M_parent->_M_stream != nullptr);
*_M_parent->_M_stream >> _M_parent->_M_object;
return *this;
}
@@ -699,21 +733,18 @@ namespace views
_Val&
operator*() const
- {
- __glibcxx_assert(_M_parent->_M_stream != nullptr);
- return _M_parent->_M_object;
- }
+ { return _M_parent->_M_object; }
friend bool
operator==(const _Iterator& __x, default_sentinel_t)
{ return __x._M_at_end(); }
private:
- basic_istream_view* _M_parent = nullptr;
+ basic_istream_view* _M_parent;
bool
_M_at_end() const
- { return _M_parent == nullptr || !*_M_parent->_M_stream; }
+ { return !*_M_parent->_M_stream; }
};
friend _Iterator;
@@ -1017,15 +1048,12 @@ namespace views::__adaptor
class ref_view : public view_interface<ref_view<_Range>>
{
private:
- _Range* _M_r = nullptr;
+ _Range* _M_r;
static void _S_fun(_Range&); // not defined
static void _S_fun(_Range&&) = delete;
public:
- constexpr
- ref_view() noexcept = default;
-
template<__detail::__not_same_as<ref_view> _Tp>
requires convertible_to<_Tp, _Range&>
&& requires { _S_fun(declval<_Tp>()); }
@@ -1205,6 +1233,16 @@ namespace views::__adaptor
return *this;
}
+ constexpr __non_propagating_cache&
+ operator=(_Tp __val)
+ {
+ this->_M_reset();
+ std::construct_at(std::__addressof(this->_M_payload._M_payload),
+ std::in_place, std::move(__val));
+ this->_M_payload._M_engaged = true;
+ return *this;
+ }
+
constexpr _Tp&
operator*() noexcept
{ return this->_M_get(); }
@@ -1382,7 +1420,7 @@ namespace views::__adaptor
using value_type = range_value_t<_Vp>;
using difference_type = range_difference_t<_Vp>;
- _Iterator() = default;
+ _Iterator() requires default_initializable<_Vp_iter> = default;
constexpr
_Iterator(filter_view* __parent, _Vp_iter __current)
@@ -1494,7 +1532,9 @@ namespace views::__adaptor
_Vp _M_base = _Vp();
public:
- filter_view() = default;
+ filter_view() requires (default_initializable<_Vp>
+ && default_initializable<_Pred>)
+ = default;
constexpr
filter_view(_Vp __base, _Pred __pred)
@@ -1643,7 +1683,7 @@ namespace views::__adaptor
= remove_cvref_t<invoke_result_t<_Fp&, range_reference_t<_Base>>>;
using difference_type = range_difference_t<_Base>;
- _Iterator() = default;
+ _Iterator() requires default_initializable<_Base_iter> = default;
constexpr
_Iterator(_Parent* __parent, _Base_iter __current)
@@ -1858,7 +1898,9 @@ namespace views::__adaptor
_Vp _M_base = _Vp();
public:
- transform_view() = default;
+ transform_view() requires (default_initializable<_Vp>
+ && default_initializable<_Fp>)
+ = default;
constexpr
transform_view(_Vp __base, _Fp __fun)
@@ -1993,7 +2035,7 @@ namespace views::__adaptor
_Vp _M_base = _Vp();
public:
- take_view() = default;
+ take_view() requires default_initializable<_Vp> = default;
constexpr
take_view(_Vp base, range_difference_t<_Vp> __count)
@@ -2177,7 +2219,9 @@ namespace views::__adaptor
_Vp _M_base = _Vp();
public:
- take_while_view() = default;
+ take_while_view() requires (default_initializable<_Vp>
+ && default_initializable<_Pred>)
+ = default;
constexpr
take_while_view(_Vp base, _Pred __pred)
@@ -2265,7 +2309,7 @@ namespace views::__adaptor
_M_cached_begin;
public:
- drop_view() = default;
+ drop_view() requires default_initializable<_Vp> = default;
constexpr
drop_view(_Vp __base, range_difference_t<_Vp> __count)
@@ -2381,7 +2425,9 @@ namespace views::__adaptor
_Vp _M_base = _Vp();
public:
- drop_while_view() = default;
+ drop_while_view() requires (default_initializable<_Vp>
+ && default_initializable<_Pred>)
+ = default;
constexpr
drop_while_view(_Vp __base, _Pred __pred)
@@ -2571,7 +2617,9 @@ namespace views::__adaptor
= common_type_t<range_difference_t<_Base>,
range_difference_t<range_reference_t<_Base>>>;
- _Iterator() = default;
+ _Iterator() requires (default_initializable<_Outer_iter>
+ && default_initializable<_Inner_iter>)
+ = default;
constexpr
_Iterator(_Parent* __parent, _Outer_iter __outer)
@@ -2724,7 +2772,7 @@ namespace views::__adaptor
_Vp _M_base = _Vp();
public:
- join_view() = default;
+ join_view() requires default_initializable<_Vp> = default;
constexpr explicit
join_view(_Vp __base)
@@ -2891,7 +2939,7 @@ namespace views::__adaptor
if constexpr (forward_range<_Vp>)
return _M_current;
else
- return _M_parent->_M_current;
+ return *_M_parent->_M_current;
}
constexpr auto&
@@ -2900,7 +2948,7 @@ namespace views::__adaptor
if constexpr (forward_range<_Vp>)
return _M_current;
else
- return _M_parent->_M_current;
+ return *_M_parent->_M_current;
}
_Parent* _M_parent = nullptr;
@@ -3146,12 +3194,14 @@ namespace views::__adaptor
// XXX: _M_current is "present only if !forward_range<V>"
[[no_unique_address]]
__detail::__maybe_present_t<!forward_range<_Vp>,
- iterator_t<_Vp>> _M_current;
+ __detail::__non_propagating_cache<iterator_t<_Vp>>> _M_current;
_Vp _M_base = _Vp();
public:
- split_view() = default;
+ split_view() requires (default_initializable<_Vp>
+ && default_initializable<_Pattern>)
+ = default;
constexpr
split_view(_Vp __base, _Pattern __pattern)
@@ -3282,7 +3332,7 @@ namespace views::__adaptor
_Vp _M_base = _Vp();
public:
- common_view() = default;
+ common_view() requires default_initializable<_Vp> = default;
constexpr explicit
common_view(_Vp __r)
@@ -3413,7 +3463,7 @@ namespace views::__adaptor
_Vp _M_base = _Vp();
public:
- reverse_view() = default;
+ reverse_view() requires default_initializable<_Vp> = default;
constexpr explicit
reverse_view(_Vp __r)
@@ -3555,7 +3605,7 @@ namespace views::__adaptor
class elements_view : public view_interface<elements_view<_Vp, _Nm>>
{
public:
- elements_view() = default;
+ elements_view() requires default_initializable<_Vp> = default;
constexpr explicit
elements_view(_Vp base)
@@ -3676,7 +3726,7 @@ namespace views::__adaptor
= remove_cvref_t<tuple_element_t<_Nm, range_value_t<_Base>>>;
using difference_type = range_difference_t<_Base>;
- _Iterator() = default;
+ _Iterator() requires default_initializable<iterator_t<_Base>> = default;
constexpr explicit
_Iterator(iterator_t<_Base> current)
diff --git a/libstdc++-v3/include/std/span b/libstdc++-v3/include/std/span
index 09bdcd69afb..63f0a8f6279 100644
--- a/libstdc++-v3/include/std/span
+++ b/libstdc++-v3/include/std/span
@@ -447,8 +447,7 @@ _GLIBCXX_BEGIN_NAMESPACE_VERSION
// Opt-in to view concept
template<typename _ElementType, size_t _Extent>
inline constexpr bool
- enable_view<span<_ElementType, _Extent>>
- = _Extent == 0 || _Extent == dynamic_extent;
+ enable_view<span<_ElementType, _Extent>> = true;
}
_GLIBCXX_END_NAMESPACE_VERSION
} // namespace std
diff --git a/libstdc++-v3/include/std/version b/libstdc++-v3/include/std/version
index 8d0b2b95f34..e3ab9b4c7c0 100644
--- a/libstdc++-v3/include/std/version
+++ b/libstdc++-v3/include/std/version
@@ -240,7 +240,7 @@
#define __cpp_lib_math_constants 201907L
#define __cpp_lib_polymorphic_allocator 201902L
#if __cpp_lib_concepts
-# define __cpp_lib_ranges 201911L
+# define __cpp_lib_ranges 202106L
#endif
#if __cpp_lib_atomic_wait || _GLIBCXX_HAVE_POSIX_SEMAPHORE
# define __cpp_lib_semaphore 201907L
diff --git a/libstdc++-v3/testsuite/24_iterators/back_insert_iterator/constexpr.cc b/libstdc++-v3/testsuite/24_iterators/back_insert_iterator/constexpr.cc
index bef2289ba79..27acd071df1 100644
--- a/libstdc++-v3/testsuite/24_iterators/back_insert_iterator/constexpr.cc
+++ b/libstdc++-v3/testsuite/24_iterators/back_insert_iterator/constexpr.cc
@@ -42,8 +42,7 @@ constexpr bool
test01()
{
container c;
- std::back_insert_iterator<container> iter;
- iter = std::back_inserter(c);
+ std::back_insert_iterator<container> iter = std::back_inserter(c);
*iter++ = 1;
int i = 2;
*iter = i;
diff --git a/libstdc++-v3/testsuite/24_iterators/front_insert_iterator/constexpr.cc b/libstdc++-v3/testsuite/24_iterators/front_insert_iterator/constexpr.cc
index 7b4c990b107..cff7f6a4524 100644
--- a/libstdc++-v3/testsuite/24_iterators/front_insert_iterator/constexpr.cc
+++ b/libstdc++-v3/testsuite/24_iterators/front_insert_iterator/constexpr.cc
@@ -42,8 +42,7 @@ constexpr bool
test01()
{
container c;
- std::front_insert_iterator<container> iter;
- iter = std::front_inserter(c);
+ std::front_insert_iterator<container> iter = std::front_inserter(c);
*iter++ = 1;
int i = 2;
*iter = i;
diff --git a/libstdc++-v3/testsuite/24_iterators/insert_iterator/constexpr.cc b/libstdc++-v3/testsuite/24_iterators/insert_iterator/constexpr.cc
index e74df3eb5d5..e326b01d534 100644
--- a/libstdc++-v3/testsuite/24_iterators/insert_iterator/constexpr.cc
+++ b/libstdc++-v3/testsuite/24_iterators/insert_iterator/constexpr.cc
@@ -51,8 +51,7 @@ constexpr bool
test01()
{
container c;
- std::insert_iterator<container> iter;
- iter = std::inserter(c, c.begin());
+ std::insert_iterator<container> iter = std::inserter(c, c.begin());
*iter++ = 1;
int i = 2;
*iter = i;
diff --git a/libstdc++-v3/testsuite/24_iterators/ostream_iterator/requirements/constexpr.cc b/libstdc++-v3/testsuite/24_iterators/ostream_iterator/requirements/constexpr.cc
deleted file mode 100644
index 4edaaa8aebb..00000000000
--- a/libstdc++-v3/testsuite/24_iterators/ostream_iterator/requirements/constexpr.cc
+++ /dev/null
@@ -1,24 +0,0 @@
-// Copyright (C) 2019-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++2a" }
-// { dg-do compile { target c++2a } }
-
-#include <iterator>
-
-constexpr std::ostream_iterator<int> iter1;
-constexpr std::ostream_iterator<int> iter2{};
diff --git a/libstdc++-v3/testsuite/std/ranges/97600.cc b/libstdc++-v3/testsuite/std/ranges/97600.cc
index 7435de022cd..c642b9d22d0 100644
--- a/libstdc++-v3/testsuite/std/ranges/97600.cc
+++ b/libstdc++-v3/testsuite/std/ranges/97600.cc
@@ -24,9 +24,8 @@
#include <ranges>
void
-test01()
+test01(std::ranges::basic_istream_view<int, char, std::char_traits<char>> v)
{
- std::ranges::basic_istream_view<int, char, std::char_traits<char>> v;
v.begin();
static_assert(std::ranges::range<decltype(v)>);
}
diff --git a/libstdc++-v3/testsuite/std/ranges/adaptors/detail/semiregular_box.cc b/libstdc++-v3/testsuite/std/ranges/adaptors/detail/copyable_box.cc
similarity index 70%
rename from libstdc++-v3/testsuite/std/ranges/adaptors/detail/semiregular_box.cc
rename to libstdc++-v3/testsuite/std/ranges/adaptors/detail/copyable_box.cc
index ed694e04fd1..fa6d4d56816 100644
--- a/libstdc++-v3/testsuite/std/ranges/adaptors/detail/semiregular_box.cc
+++ b/libstdc++-v3/testsuite/std/ranges/adaptors/detail/copyable_box.cc
@@ -82,9 +82,10 @@ test01()
}
static_assert(test01());
-template<bool make_semiregular>
+template<bool make_copyable>
struct A {
- A() requires make_semiregular;
+ A(const A&) = default;
+ A& operator=(const A&) requires make_copyable;
A(int, int);
A(std::initializer_list<int>) = delete;
};
@@ -93,9 +94,51 @@ void
test02()
{
// PR libstdc++/100475
- static_assert(std::semiregular<A<true>>);
+ static_assert(std::copyable<A<true>>);
__box<A<true>> x2(std::in_place, 0, 0);
- static_assert(!std::semiregular<A<false>>);
+ static_assert(!std::copyable<A<false>>);
__box<A<false>> x1(std::in_place, 0, 0);
}
+
+constexpr bool
+test03()
+{
+ // Verify correctness of the non-defaulted operator= for the partial
+ // specialization of __box.
+ struct B {
+ constexpr B(int* p) : p(p) { }
+ constexpr ~B() { ++*p; };
+ B(const B&) = default;
+ B& operator=(const B&) = delete;
+ int* p;
+ };
+ static_assert(!std::copyable<B>);
+ static_assert(std::is_nothrow_copy_constructible_v<B>);
+ static_assert(sizeof(__box<B>) == sizeof(B));
+
+ int m = 0;
+ __box<B> x(std::in_place, &m);
+ __glibcxx_assert(m == 0);
+ x = x;
+ __glibcxx_assert(m == 0);
+ x = std::move(x);
+ __glibcxx_assert(m == 0);
+
+ int n = 0;
+ __box<B> y(std::in_place, &n);
+ auto z = x;
+ x = y;
+ __glibcxx_assert(m == 1);
+ __glibcxx_assert(n == 0);
+ __glibcxx_assert(x->p == &n);
+ __glibcxx_assert(y->p == &n);
+ y = std::move(z);
+ __glibcxx_assert(m == 1);
+ __glibcxx_assert(n == 1);
+ __glibcxx_assert(y->p == &m);
+ __glibcxx_assert(z->p == &m);
+
+ return true;
+}
+static_assert(test03());
diff --git a/libstdc++-v3/testsuite/std/ranges/p2325.cc b/libstdc++-v3/testsuite/std/ranges/p2325.cc
new file mode 100644
index 00000000000..df6cde29e4d
--- /dev/null
+++ b/libstdc++-v3/testsuite/std/ranges/p2325.cc
@@ -0,0 +1,155 @@
+// { dg-options "-std=gnu++20" }
+// { dg-do compile { target c++20 } }
+// P2325R3 "Views should not be required to be default constructible"
+
+#include <ranges>
+#include <iterator>
+#include <span>
+#include <sstream>
+#include <vector>
+#include <testsuite_iterators.h>
+
+using namespace std;
+
+template<default_initializable T> void f();
+template<typename T> requires weakly_incrementable<T> || ranges::view<T> void f();
+
+void
+test01()
+{
+ // Verify neither std::weakly_incrementable nor ranges::view require
+ // default_initializable.
+ f<int>(); // { dg-error "ambiguous" }
+}
+
+void
+test02()
+{
+ // Verify these iterators are not default constructible.
+ static_assert(!default_initializable<insert_iterator<vector<int>>>);
+ static_assert(!default_initializable<front_insert_iterator<vector<int>>>);
+ static_assert(!default_initializable<back_insert_iterator<vector<int>>>);
+ static_assert(!default_initializable<ostream_iterator<int>>);
+
+ using iter = ostream_iterator<int>;
+
+ // Verify common_iterator is conditionally default constructible.
+ static_assert(!default_initializable<common_iterator<iter, unreachable_sentinel_t>>);
+ static_assert(default_initializable<common_iterator<int*, unreachable_sentinel_t>>);
+
+ // Verify counted_iterator is conditionally default constructible.
+ static_assert(!default_initializable<counted_iterator<iter>>);
+ static_assert(default_initializable<counted_iterator<int*>>);
+}
+
+void
+test03()
+{
+ using iter = ostream_iterator<int>;
+
+ // Verify iota_view is conditionally default constructible.
+ static_assert(!default_initializable<ranges::iota_view<iter>>);
+ static_assert(!default_initializable<decltype(declval<ranges::iota_view<iter>>().begin())>);
+ static_assert(default_initializable<ranges::iota_view<int>>);
+ static_assert(default_initializable<decltype(declval<ranges::iota_view<int>>().begin())>);
+
+ // Verify subrange is conditionally default constructible.
+ static_assert(!default_initializable<ranges::subrange<iter, unreachable_sentinel_t>>);
+ static_assert(default_initializable<ranges::subrange<int*, unreachable_sentinel_t>>);
+
+ // Verify single_view is conditionally default constructible.
+ static_assert(!default_initializable<ranges::single_view<iter>>);
+ static_assert(default_initializable<ranges::single_view<int*>>);
+}
+
+void
+test04()
+{
+ // Verify basic_istream_view is not default constructible.
+ using type = ranges::basic_istream_view<int, char, char_traits<char>>;
+ static_assert(!default_initializable<type>);
+ static_assert(!default_initializable<decltype(declval<type>().begin())>);
+}
+
+void
+test05()
+{
+ // Verify ref_view is not default constructible.
+ static_assert(!default_initializable<ranges::ref_view<int[5]>>);
+}
+
+template<auto adaptor>
+void
+test06()
+{
+ auto f1 = [] (auto) { return true; };
+ auto f2 = [i=0] (auto) { return true; };
+ static_assert(default_initializable<decltype(views::single(0) | adaptor(f1))>);
+ static_assert(!default_initializable<decltype(views::single(0) | adaptor(f2))>);
+
+ struct S { S() = delete; };
+ static_assert(!default_initializable<decltype(views::single(declval<S>()) | adaptor(f1))>);
+ static_assert(!default_initializable<decltype(views::single(declval<S>()) | adaptor(f2))>);
+}
+
+// Verify filter_view, transform_view, take_while_view and drop_while_view are
+// conditionally default constructible.
+template void test06<views::filter>();
+template void test06<views::transform>();
+template void test06<views::take_while>();
+template void test06<views::drop_while>();
+
+void
+test07()
+{
+ // Verify join_view is conditionally default constructible.
+ struct S { S() = delete; };
+ using type1 = ranges::join_view<ranges::single_view<ranges::single_view<S>>>;
+ static_assert(!default_initializable<type1>);
+ using type2 = ranges::join_view<ranges::single_view<ranges::single_view<int>>>;
+ static_assert(default_initializable<type2>);
+}
+
+void
+test08()
+{
+ // Verify split_view is conditionally default constructible.
+ using type1 = ranges::split_view<ranges::ref_view<int[2]>, ranges::single_view<int>>;
+ static_assert(!default_initializable<type1>);
+ using type2 = ranges::split_view<ranges::single_view<int>, ranges::ref_view<int[2]>>;
+ static_assert(!default_initializable<type2>);
+ using type3 = ranges::split_view<ranges::ref_view<int[2]>, ranges::ref_view<int[2]>>;
+ static_assert(!default_initializable<type3>);
+ using type4 = ranges::split_view<ranges::single_view<int>, ranges::single_view<int>>;
+ static_assert(default_initializable<type4>);
+}
+
+void
+test09()
+{
+ // Verify common_view is conditionally default constructible.
+ using type1 = ranges::common_view<ranges::iota_view<ostream_iterator<int>>>;
+ static_assert(!default_initializable<type1>);
+ using type2 = ranges::common_view<ranges::iota_view<int*>>;
+ static_assert(default_initializable<type2>);
+}
+
+void
+test10()
+{
+ // Verify reverse_view is conditionally default constructible.
+ using type1 = ranges::reverse_view<ranges::ref_view<int[2]>>;
+ static_assert(!default_initializable<type1>);
+ using type2 = ranges::reverse_view<ranges::single_view<int>>;
+ static_assert(default_initializable<type2>);
+}
+
+void
+test11()
+{
+ // Verify elements_view is conditionally default constructible.
+ using type1 = ranges::elements_view<ranges::ref_view<pair<int,int>[2]>, 0>;
+ static_assert(!default_initializable<type1>);
+ using type2 = ranges::elements_view<ranges::single_view<pair<int,int>>, 0>;
+ static_assert(default_initializable<type2>);
+}
diff --git a/libstdc++-v3/testsuite/std/ranges/single_view.cc b/libstdc++-v3/testsuite/std/ranges/single_view.cc
index c036fc8976a..f1d8e103715 100644
--- a/libstdc++-v3/testsuite/std/ranges/single_view.cc
+++ b/libstdc++-v3/testsuite/std/ranges/single_view.cc
@@ -96,6 +96,20 @@ test05()
static_assert(noexcept(cs.empty())); // view_interface::empty()
}
+void
+test06()
+{
+ // PR libstdc++/100475 comment #7
+ struct S {
+ S() = default;
+ S(std::initializer_list<S>) = delete;
+ S(const S&) {}
+ };
+ S obj;
+ auto x = std::views::single(obj);
+ auto y = std::views::single(std::move(obj));
+}
+
int main()
{
test01();
@@ -103,4 +117,5 @@ int main()
test03();
test04();
test05();
+ test06();
}
diff --git a/libstdc++-v3/testsuite/std/ranges/view.cc b/libstdc++-v3/testsuite/std/ranges/view.cc
index d8972ab3e46..dd8258220ed 100644
--- a/libstdc++-v3/testsuite/std/ranges/view.cc
+++ b/libstdc++-v3/testsuite/std/ranges/view.cc
@@ -31,7 +31,7 @@
static_assert(std::ranges::view<std::span<int>>);
static_assert(std::ranges::view<std::span<int, 0>>);
-static_assert(!std::ranges::view<std::span<int, 1>>);
+static_assert(std::ranges::view<std::span<int, 1>>); // Changed with P2325R3
static_assert(std::ranges::view<std::string_view>);
static_assert(std::ranges::view<std::experimental::string_view>);
--
2.32.0.93.g670b81a890
^ permalink raw reply [flat|nested] 11+ messages in thread
* [PATCH 2/5] libstdc++: Move ranges algos used by <ranges> into ranges_util.h
2021-06-17 15:22 [PATCH 1/5] libstdc++: Implement P2325 changes to default-constructibilty of views Patrick Palka
@ 2021-06-17 15:22 ` Patrick Palka
2021-06-17 18:14 ` Jonathan Wakely
2021-06-17 15:22 ` [PATCH 3/5] libstdc++: Rename views::split to views::lazy_split as per P2210 Patrick Palka
` (3 subsequent siblings)
4 siblings, 1 reply; 11+ messages in thread
From: Patrick Palka @ 2021-06-17 15:22 UTC (permalink / raw)
To: gcc-patches; +Cc: libstdc++, Patrick Palka
The <ranges> header defines simplified copies of some ranges algorithms
in order to avoid including the entirety of ranges_algo.h. A subsequent
patch is going to want to use ranges::search in <ranges> as well, but
that algorithm is more complicated compared to the other copied ones.
So rather than additionally copying ranges::search into <ranges>, this
patch splits out all the ranges algos used by <ranges> (including
ranges::search) from ranges_algo.h to ranges_util.h, and deletes the
simplified copies in <ranges>. This seems like the best place for
these algorithms, as ranges_util.h is included only from <ranges> and
ranges_algo.h.
libstdc++-v3/ChangeLog:
* include/bits/ranges_algo.h (__find_fn, find, __find_if_fn)
(find_if, __find_if_not_fn, find_if_not, _in_in_result)
(__mismatch_fn, mismatch, __search_fn, search): Move to ...
* include/bits/ranges_util.h: ... here.
* include/std/ranges (__detail::find, __detail::find_if)
(__detail::find_if_not, __detail::mismatch): Remove.
(filter_view): Use ranges::find_if instead.
(drop_while_view): Use ranges::find_if_not instead.
(split_view): Use ranges::find and ranges::mismatch instead.
---
libstdc++-v3/include/bits/ranges_algo.h | 215 +----------------------
libstdc++-v3/include/bits/ranges_util.h | 219 ++++++++++++++++++++++++
libstdc++-v3/include/std/ranges | 72 ++------
3 files changed, 233 insertions(+), 273 deletions(-)
diff --git a/libstdc++-v3/include/bits/ranges_algo.h b/libstdc++-v3/include/bits/ranges_algo.h
index ecf1378742d..9eeebff6525 100644
--- a/libstdc++-v3/include/bits/ranges_algo.h
+++ b/libstdc++-v3/include/bits/ranges_algo.h
@@ -234,91 +234,7 @@ namespace ranges
inline constexpr __for_each_n_fn for_each_n{};
- struct __find_fn
- {
- template<input_iterator _Iter, sentinel_for<_Iter> _Sent, typename _Tp,
- typename _Proj = identity>
- requires indirect_binary_predicate<ranges::equal_to,
- projected<_Iter, _Proj>, const _Tp*>
- constexpr _Iter
- operator()(_Iter __first, _Sent __last,
- const _Tp& __value, _Proj __proj = {}) const
- {
- while (__first != __last
- && !(std::__invoke(__proj, *__first) == __value))
- ++__first;
- return __first;
- }
-
- template<input_range _Range, typename _Tp, typename _Proj = identity>
- requires indirect_binary_predicate<ranges::equal_to,
- projected<iterator_t<_Range>, _Proj>,
- const _Tp*>
- constexpr borrowed_iterator_t<_Range>
- operator()(_Range&& __r, const _Tp& __value, _Proj __proj = {}) const
- {
- return (*this)(ranges::begin(__r), ranges::end(__r),
- __value, std::move(__proj));
- }
- };
-
- inline constexpr __find_fn find{};
-
- struct __find_if_fn
- {
- template<input_iterator _Iter, sentinel_for<_Iter> _Sent,
- typename _Proj = identity,
- indirect_unary_predicate<projected<_Iter, _Proj>> _Pred>
- constexpr _Iter
- operator()(_Iter __first, _Sent __last,
- _Pred __pred, _Proj __proj = {}) const
- {
- while (__first != __last
- && !(bool)std::__invoke(__pred, std::__invoke(__proj, *__first)))
- ++__first;
- return __first;
- }
-
- template<input_range _Range, typename _Proj = identity,
- indirect_unary_predicate<projected<iterator_t<_Range>, _Proj>>
- _Pred>
- constexpr borrowed_iterator_t<_Range>
- operator()(_Range&& __r, _Pred __pred, _Proj __proj = {}) const
- {
- return (*this)(ranges::begin(__r), ranges::end(__r),
- std::move(__pred), std::move(__proj));
- }
- };
-
- inline constexpr __find_if_fn find_if{};
-
- struct __find_if_not_fn
- {
- template<input_iterator _Iter, sentinel_for<_Iter> _Sent,
- typename _Proj = identity,
- indirect_unary_predicate<projected<_Iter, _Proj>> _Pred>
- constexpr _Iter
- operator()(_Iter __first, _Sent __last,
- _Pred __pred, _Proj __proj = {}) const
- {
- while (__first != __last
- && (bool)std::__invoke(__pred, std::__invoke(__proj, *__first)))
- ++__first;
- return __first;
- }
-
- template<input_range _Range, typename _Proj = identity,
- indirect_unary_predicate<projected<iterator_t<_Range>, _Proj>>
- _Pred>
- constexpr borrowed_iterator_t<_Range>
- operator()(_Range&& __r, _Pred __pred, _Proj __proj = {}) const
- {
- return (*this)(ranges::begin(__r), ranges::end(__r),
- std::move(__pred), std::move(__proj));
- }
- };
-
- inline constexpr __find_if_not_fn find_if_not{};
+ // find, find_if and find_if_not are defined in <bits/ranges_util.h>.
struct __find_first_of_fn
{
@@ -421,134 +337,7 @@ namespace ranges
inline constexpr __count_if_fn count_if{};
- template<typename _Iter1, typename _Iter2>
- struct in_in_result
- {
- [[no_unique_address]] _Iter1 in1;
- [[no_unique_address]] _Iter2 in2;
-
- template<typename _IIter1, typename _IIter2>
- requires convertible_to<const _Iter1&, _IIter1>
- && convertible_to<const _Iter2&, _IIter2>
- constexpr
- operator in_in_result<_IIter1, _IIter2>() const &
- { return {in1, in2}; }
-
- template<typename _IIter1, typename _IIter2>
- requires convertible_to<_Iter1, _IIter1>
- && convertible_to<_Iter2, _IIter2>
- constexpr
- operator in_in_result<_IIter1, _IIter2>() &&
- { return {std::move(in1), std::move(in2)}; }
- };
-
- template<typename _Iter1, typename _Iter2>
- using mismatch_result = in_in_result<_Iter1, _Iter2>;
-
- struct __mismatch_fn
- {
- template<input_iterator _Iter1, sentinel_for<_Iter1> _Sent1,
- input_iterator _Iter2, sentinel_for<_Iter2> _Sent2,
- typename _Pred = ranges::equal_to,
- typename _Proj1 = identity, typename _Proj2 = identity>
- requires indirectly_comparable<_Iter1, _Iter2, _Pred, _Proj1, _Proj2>
- constexpr mismatch_result<_Iter1, _Iter2>
- operator()(_Iter1 __first1, _Sent1 __last1,
- _Iter2 __first2, _Sent2 __last2, _Pred __pred = {},
- _Proj1 __proj1 = {}, _Proj2 __proj2 = {}) const
- {
- while (__first1 != __last1 && __first2 != __last2
- && (bool)std::__invoke(__pred,
- std::__invoke(__proj1, *__first1),
- std::__invoke(__proj2, *__first2)))
- {
- ++__first1;
- ++__first2;
- }
- return { std::move(__first1), std::move(__first2) };
- }
-
- template<input_range _Range1, input_range _Range2,
- typename _Pred = ranges::equal_to,
- typename _Proj1 = identity, typename _Proj2 = identity>
- requires indirectly_comparable<iterator_t<_Range1>, iterator_t<_Range2>,
- _Pred, _Proj1, _Proj2>
- constexpr mismatch_result<iterator_t<_Range1>, iterator_t<_Range2>>
- operator()(_Range1&& __r1, _Range2&& __r2, _Pred __pred = {},
- _Proj1 __proj1 = {}, _Proj2 __proj2 = {}) const
- {
- return (*this)(ranges::begin(__r1), ranges::end(__r1),
- ranges::begin(__r2), ranges::end(__r2),
- std::move(__pred),
- std::move(__proj1), std::move(__proj2));
- }
- };
-
- inline constexpr __mismatch_fn mismatch{};
-
- struct __search_fn
- {
- template<forward_iterator _Iter1, sentinel_for<_Iter1> _Sent1,
- forward_iterator _Iter2, sentinel_for<_Iter2> _Sent2,
- typename _Pred = ranges::equal_to,
- typename _Proj1 = identity, typename _Proj2 = identity>
- requires indirectly_comparable<_Iter1, _Iter2, _Pred, _Proj1, _Proj2>
- constexpr subrange<_Iter1>
- operator()(_Iter1 __first1, _Sent1 __last1,
- _Iter2 __first2, _Sent2 __last2, _Pred __pred = {},
- _Proj1 __proj1 = {}, _Proj2 __proj2 = {}) const
- {
- if (__first1 == __last1 || __first2 == __last2)
- return {__first1, __first1};
-
- for (;;)
- {
- for (;;)
- {
- if (__first1 == __last1)
- return {__first1, __first1};
- if (std::__invoke(__pred,
- std::__invoke(__proj1, *__first1),
- std::__invoke(__proj2, *__first2)))
- break;
- ++__first1;
- }
- auto __cur1 = __first1;
- auto __cur2 = __first2;
- for (;;)
- {
- if (++__cur2 == __last2)
- return {__first1, ++__cur1};
- if (++__cur1 == __last1)
- return {__cur1, __cur1};
- if (!(bool)std::__invoke(__pred,
- std::__invoke(__proj1, *__cur1),
- std::__invoke(__proj2, *__cur2)))
- {
- ++__first1;
- break;
- }
- }
- }
- }
-
- template<forward_range _Range1, forward_range _Range2,
- typename _Pred = ranges::equal_to,
- typename _Proj1 = identity, typename _Proj2 = identity>
- requires indirectly_comparable<iterator_t<_Range1>, iterator_t<_Range2>,
- _Pred, _Proj1, _Proj2>
- constexpr borrowed_subrange_t<_Range1>
- operator()(_Range1&& __r1, _Range2&& __r2, _Pred __pred = {},
- _Proj1 __proj1 = {}, _Proj2 __proj2 = {}) const
- {
- return (*this)(ranges::begin(__r1), ranges::end(__r1),
- ranges::begin(__r2), ranges::end(__r2),
- std::move(__pred),
- std::move(__proj1), std::move(__proj2));
- }
- };
-
- inline constexpr __search_fn search{};
+ // in_in_result, mismatch and search are defined in <bits/ranges_util.h>.
struct __search_n_fn
{
diff --git a/libstdc++-v3/include/bits/ranges_util.h b/libstdc++-v3/include/bits/ranges_util.h
index d7b12b3d985..9a07079ac13 100644
--- a/libstdc++-v3/include/bits/ranges_util.h
+++ b/libstdc++-v3/include/bits/ranges_util.h
@@ -420,7 +420,226 @@ namespace ranges
using borrowed_subrange_t = conditional_t<borrowed_range<_Range>,
subrange<iterator_t<_Range>>,
dangling>;
+} // namespace ranges
+
+// The following ranges algorithms are used by <ranges>, and are defined here
+// so that <ranges> can avoid including all of <bits/ranges_algo.h>.
+namespace ranges
+{
+ struct __find_fn
+ {
+ template<input_iterator _Iter, sentinel_for<_Iter> _Sent, typename _Tp,
+ typename _Proj = identity>
+ requires indirect_binary_predicate<ranges::equal_to,
+ projected<_Iter, _Proj>, const _Tp*>
+ constexpr _Iter
+ operator()(_Iter __first, _Sent __last,
+ const _Tp& __value, _Proj __proj = {}) const
+ {
+ while (__first != __last
+ && !(std::__invoke(__proj, *__first) == __value))
+ ++__first;
+ return __first;
+ }
+
+ template<input_range _Range, typename _Tp, typename _Proj = identity>
+ requires indirect_binary_predicate<ranges::equal_to,
+ projected<iterator_t<_Range>, _Proj>,
+ const _Tp*>
+ constexpr borrowed_iterator_t<_Range>
+ operator()(_Range&& __r, const _Tp& __value, _Proj __proj = {}) const
+ {
+ return (*this)(ranges::begin(__r), ranges::end(__r),
+ __value, std::move(__proj));
+ }
+ };
+
+ inline constexpr __find_fn find{};
+
+ struct __find_if_fn
+ {
+ template<input_iterator _Iter, sentinel_for<_Iter> _Sent,
+ typename _Proj = identity,
+ indirect_unary_predicate<projected<_Iter, _Proj>> _Pred>
+ constexpr _Iter
+ operator()(_Iter __first, _Sent __last,
+ _Pred __pred, _Proj __proj = {}) const
+ {
+ while (__first != __last
+ && !(bool)std::__invoke(__pred, std::__invoke(__proj, *__first)))
+ ++__first;
+ return __first;
+ }
+
+ template<input_range _Range, typename _Proj = identity,
+ indirect_unary_predicate<projected<iterator_t<_Range>, _Proj>>
+ _Pred>
+ constexpr borrowed_iterator_t<_Range>
+ operator()(_Range&& __r, _Pred __pred, _Proj __proj = {}) const
+ {
+ return (*this)(ranges::begin(__r), ranges::end(__r),
+ std::move(__pred), std::move(__proj));
+ }
+ };
+
+ inline constexpr __find_if_fn find_if{};
+
+ struct __find_if_not_fn
+ {
+ template<input_iterator _Iter, sentinel_for<_Iter> _Sent,
+ typename _Proj = identity,
+ indirect_unary_predicate<projected<_Iter, _Proj>> _Pred>
+ constexpr _Iter
+ operator()(_Iter __first, _Sent __last,
+ _Pred __pred, _Proj __proj = {}) const
+ {
+ while (__first != __last
+ && (bool)std::__invoke(__pred, std::__invoke(__proj, *__first)))
+ ++__first;
+ return __first;
+ }
+
+ template<input_range _Range, typename _Proj = identity,
+ indirect_unary_predicate<projected<iterator_t<_Range>, _Proj>>
+ _Pred>
+ constexpr borrowed_iterator_t<_Range>
+ operator()(_Range&& __r, _Pred __pred, _Proj __proj = {}) const
+ {
+ return (*this)(ranges::begin(__r), ranges::end(__r),
+ std::move(__pred), std::move(__proj));
+ }
+ };
+
+ inline constexpr __find_if_not_fn find_if_not{};
+
+ template<typename _Iter1, typename _Iter2>
+ struct in_in_result
+ {
+ [[no_unique_address]] _Iter1 in1;
+ [[no_unique_address]] _Iter2 in2;
+
+ template<typename _IIter1, typename _IIter2>
+ requires convertible_to<const _Iter1&, _IIter1>
+ && convertible_to<const _Iter2&, _IIter2>
+ constexpr
+ operator in_in_result<_IIter1, _IIter2>() const &
+ { return {in1, in2}; }
+
+ template<typename _IIter1, typename _IIter2>
+ requires convertible_to<_Iter1, _IIter1>
+ && convertible_to<_Iter2, _IIter2>
+ constexpr
+ operator in_in_result<_IIter1, _IIter2>() &&
+ { return {std::move(in1), std::move(in2)}; }
+ };
+
+ template<typename _Iter1, typename _Iter2>
+ using mismatch_result = in_in_result<_Iter1, _Iter2>;
+
+ struct __mismatch_fn
+ {
+ template<input_iterator _Iter1, sentinel_for<_Iter1> _Sent1,
+ input_iterator _Iter2, sentinel_for<_Iter2> _Sent2,
+ typename _Pred = ranges::equal_to,
+ typename _Proj1 = identity, typename _Proj2 = identity>
+ requires indirectly_comparable<_Iter1, _Iter2, _Pred, _Proj1, _Proj2>
+ constexpr mismatch_result<_Iter1, _Iter2>
+ operator()(_Iter1 __first1, _Sent1 __last1,
+ _Iter2 __first2, _Sent2 __last2, _Pred __pred = {},
+ _Proj1 __proj1 = {}, _Proj2 __proj2 = {}) const
+ {
+ while (__first1 != __last1 && __first2 != __last2
+ && (bool)std::__invoke(__pred,
+ std::__invoke(__proj1, *__first1),
+ std::__invoke(__proj2, *__first2)))
+ {
+ ++__first1;
+ ++__first2;
+ }
+ return { std::move(__first1), std::move(__first2) };
+ }
+
+ template<input_range _Range1, input_range _Range2,
+ typename _Pred = ranges::equal_to,
+ typename _Proj1 = identity, typename _Proj2 = identity>
+ requires indirectly_comparable<iterator_t<_Range1>, iterator_t<_Range2>,
+ _Pred, _Proj1, _Proj2>
+ constexpr mismatch_result<iterator_t<_Range1>, iterator_t<_Range2>>
+ operator()(_Range1&& __r1, _Range2&& __r2, _Pred __pred = {},
+ _Proj1 __proj1 = {}, _Proj2 __proj2 = {}) const
+ {
+ return (*this)(ranges::begin(__r1), ranges::end(__r1),
+ ranges::begin(__r2), ranges::end(__r2),
+ std::move(__pred),
+ std::move(__proj1), std::move(__proj2));
+ }
+ };
+
+ inline constexpr __mismatch_fn mismatch{};
+
+ struct __search_fn
+ {
+ template<forward_iterator _Iter1, sentinel_for<_Iter1> _Sent1,
+ forward_iterator _Iter2, sentinel_for<_Iter2> _Sent2,
+ typename _Pred = ranges::equal_to,
+ typename _Proj1 = identity, typename _Proj2 = identity>
+ requires indirectly_comparable<_Iter1, _Iter2, _Pred, _Proj1, _Proj2>
+ constexpr subrange<_Iter1>
+ operator()(_Iter1 __first1, _Sent1 __last1,
+ _Iter2 __first2, _Sent2 __last2, _Pred __pred = {},
+ _Proj1 __proj1 = {}, _Proj2 __proj2 = {}) const
+ {
+ if (__first1 == __last1 || __first2 == __last2)
+ return {__first1, __first1};
+
+ for (;;)
+ {
+ for (;;)
+ {
+ if (__first1 == __last1)
+ return {__first1, __first1};
+ if (std::__invoke(__pred,
+ std::__invoke(__proj1, *__first1),
+ std::__invoke(__proj2, *__first2)))
+ break;
+ ++__first1;
+ }
+ auto __cur1 = __first1;
+ auto __cur2 = __first2;
+ for (;;)
+ {
+ if (++__cur2 == __last2)
+ return {__first1, ++__cur1};
+ if (++__cur1 == __last1)
+ return {__cur1, __cur1};
+ if (!(bool)std::__invoke(__pred,
+ std::__invoke(__proj1, *__cur1),
+ std::__invoke(__proj2, *__cur2)))
+ {
+ ++__first1;
+ break;
+ }
+ }
+ }
+ }
+
+ template<forward_range _Range1, forward_range _Range2,
+ typename _Pred = ranges::equal_to,
+ typename _Proj1 = identity, typename _Proj2 = identity>
+ requires indirectly_comparable<iterator_t<_Range1>, iterator_t<_Range2>,
+ _Pred, _Proj1, _Proj2>
+ constexpr borrowed_subrange_t<_Range1>
+ operator()(_Range1&& __r1, _Range2&& __r2, _Pred __pred = {},
+ _Proj1 __proj1 = {}, _Proj2 __proj2 = {}) const
+ {
+ return (*this)(ranges::begin(__r1), ranges::end(__r1),
+ ranges::begin(__r2), ranges::end(__r2),
+ std::move(__pred),
+ std::move(__proj1), std::move(__proj2));
+ }
+ };
+ inline constexpr __search_fn search{};
} // namespace ranges
using ranges::get;
diff --git a/libstdc++-v3/include/std/ranges b/libstdc++-v3/include/std/ranges
index f96adf63d10..f93a880ff8a 100644
--- a/libstdc++-v3/include/std/ranges
+++ b/libstdc++-v3/include/std/ranges
@@ -1144,54 +1144,6 @@ namespace views::__adaptor
using all_t = decltype(all(std::declval<_Range>()));
} // namespace views
- // The following simple algos are transcribed from ranges_algo.h to avoid
- // having to include that entire header.
- namespace __detail
- {
- template<typename _Iter, typename _Sent, typename _Tp>
- constexpr _Iter
- find(_Iter __first, _Sent __last, const _Tp& __value)
- {
- while (__first != __last
- && !(bool)(*__first == __value))
- ++__first;
- return __first;
- }
-
- template<typename _Iter, typename _Sent, typename _Pred>
- constexpr _Iter
- find_if(_Iter __first, _Sent __last, _Pred __pred)
- {
- while (__first != __last
- && !(bool)std::__invoke(__pred, *__first))
- ++__first;
- return __first;
- }
-
- template<typename _Iter, typename _Sent, typename _Pred>
- constexpr _Iter
- find_if_not(_Iter __first, _Sent __last, _Pred __pred)
- {
- while (__first != __last
- && (bool)std::__invoke(__pred, *__first))
- ++__first;
- return __first;
- }
-
- template<typename _Iter1, typename _Sent1, typename _Iter2, typename _Sent2>
- constexpr pair<_Iter1, _Iter2>
- mismatch(_Iter1 __first1, _Sent1 __last1, _Iter2 __first2, _Sent2 __last2)
- {
- while (__first1 != __last1 && __first2 != __last2
- && (bool)ranges::equal_to{}(*__first1, *__first2))
- {
- ++__first1;
- ++__first2;
- }
- return { std::move(__first1), std::move(__first2) };
- }
- } // namespace __detail
-
namespace __detail
{
template<typename _Tp>
@@ -1449,9 +1401,9 @@ namespace views::__adaptor
constexpr _Iterator&
operator++()
{
- _M_current = __detail::find_if(std::move(++_M_current),
- ranges::end(_M_parent->_M_base),
- std::ref(*_M_parent->_M_pred));
+ _M_current = ranges::find_if(std::move(++_M_current),
+ ranges::end(_M_parent->_M_base),
+ std::ref(*_M_parent->_M_pred));
return *this;
}
@@ -1560,9 +1512,9 @@ namespace views::__adaptor
return {this, _M_cached_begin._M_get(_M_base)};
__glibcxx_assert(_M_pred.has_value());
- auto __it = __detail::find_if(ranges::begin(_M_base),
- ranges::end(_M_base),
- std::ref(*_M_pred));
+ auto __it = ranges::find_if(ranges::begin(_M_base),
+ ranges::end(_M_base),
+ std::ref(*_M_pred));
_M_cached_begin._M_set(_M_base, __it);
return {this, std::move(__it)};
}
@@ -2453,9 +2405,9 @@ namespace views::__adaptor
return _M_cached_begin._M_get(_M_base);
__glibcxx_assert(_M_pred.has_value());
- auto __it = __detail::find_if_not(ranges::begin(_M_base),
- ranges::end(_M_base),
- std::cref(*_M_pred));
+ auto __it = ranges::find_if_not(ranges::begin(_M_base),
+ ranges::end(_M_base),
+ std::cref(*_M_pred));
_M_cached_begin._M_set(_M_base, __it);
return __it;
}
@@ -3031,8 +2983,8 @@ namespace views::__adaptor
++__current();
else if constexpr (__detail::__tiny_range<_Pattern>)
{
- __current() = __detail::find(std::move(__current()), __end,
- *__pbegin);
+ __current() = ranges::find(std::move(__current()), __end,
+ *__pbegin);
if (__current() != __end)
++__current();
}
@@ -3040,7 +2992,7 @@ namespace views::__adaptor
do
{
auto [__b, __p]
- = __detail::mismatch(__current(), __end, __pbegin, __pend);
+ = ranges::mismatch(__current(), __end, __pbegin, __pend);
if (__p == __pend)
{
__current() = __b;
--
2.32.0.93.g670b81a890
^ permalink raw reply [flat|nested] 11+ messages in thread
* [PATCH 3/5] libstdc++: Rename views::split to views::lazy_split as per P2210
2021-06-17 15:22 [PATCH 1/5] libstdc++: Implement P2325 changes to default-constructibilty of views Patrick Palka
2021-06-17 15:22 ` [PATCH 2/5] libstdc++: Move ranges algos used by <ranges> into ranges_util.h Patrick Palka
@ 2021-06-17 15:22 ` Patrick Palka
2021-06-18 21:56 ` Jonathan Wakely
2021-06-17 15:22 ` [PATCH 4/5] libstdc++: Implement resolution of LWG 3478 " Patrick Palka
` (2 subsequent siblings)
4 siblings, 1 reply; 11+ messages in thread
From: Patrick Palka @ 2021-06-17 15:22 UTC (permalink / raw)
To: gcc-patches; +Cc: libstdc++, Patrick Palka
This mostly mechanical patch performs the renaming part of P2210R3
"Superior string splitting". It also defines _InnerIter::base()
overloads.
libstdc++-v3/ChangeLog:
* include/std/ranges: Rename views::split to views::lazy_split,
split_view to lazy_split_view, etc. throughout.
(lazy_split_view::_InnerIter::base): Define as per P2210.
* testsuite/std/ranges/*: Likewise.
---
libstdc++-v3/include/std/ranges | 68 +++++++++++--------
.../testsuite/std/ranges/adaptors/100479.cc | 2 +-
.../testsuite/std/ranges/adaptors/100577.cc | 20 +++---
.../testsuite/std/ranges/adaptors/join.cc | 2 +-
.../adaptors/{split.cc => lazy_split.cc} | 54 +++++++--------
.../{split_neg.cc => lazy_split_neg.cc} | 6 +-
.../testsuite/std/ranges/adaptors/p2281.cc | 18 ++---
.../testsuite/std/ranges/adaptors/sizeof.cc | 2 +-
libstdc++-v3/testsuite/std/ranges/p2259.cc | 6 +-
libstdc++-v3/testsuite/std/ranges/p2325.cc | 10 +--
libstdc++-v3/testsuite/std/ranges/p2367.cc | 4 +-
11 files changed, 100 insertions(+), 92 deletions(-)
rename libstdc++-v3/testsuite/std/ranges/adaptors/{split.cc => lazy_split.cc} (76%)
rename libstdc++-v3/testsuite/std/ranges/adaptors/{split_neg.cc => lazy_split_neg.cc} (79%)
diff --git a/libstdc++-v3/include/std/ranges b/libstdc++-v3/include/std/ranges
index f93a880ff8a..cc1ef112ff1 100644
--- a/libstdc++-v3/include/std/ranges
+++ b/libstdc++-v3/include/std/ranges
@@ -2826,19 +2826,19 @@ namespace views::__adaptor
&& (remove_reference_t<_Range>::size() <= 1);
template<typename _Base>
- struct __split_view_outer_iter_cat
+ struct __lazy_split_view_outer_iter_cat
{ };
template<forward_range _Base>
- struct __split_view_outer_iter_cat<_Base>
+ struct __lazy_split_view_outer_iter_cat<_Base>
{ using iterator_category = input_iterator_tag; };
template<typename _Base>
- struct __split_view_inner_iter_cat
+ struct __lazy_split_view_inner_iter_cat
{ };
template<forward_range _Base>
- struct __split_view_inner_iter_cat<_Base>
+ struct __lazy_split_view_inner_iter_cat<_Base>
{
private:
static constexpr auto
@@ -2860,7 +2860,7 @@ namespace views::__adaptor
&& indirectly_comparable<iterator_t<_Vp>, iterator_t<_Pattern>,
ranges::equal_to>
&& (forward_range<_Vp> || __detail::__tiny_range<_Pattern>)
- class split_view : public view_interface<split_view<_Vp, _Pattern>>
+ class lazy_split_view : public view_interface<lazy_split_view<_Vp, _Pattern>>
{
private:
template<bool _Const>
@@ -2871,17 +2871,17 @@ namespace views::__adaptor
template<bool _Const>
struct _OuterIter
- : __detail::__split_view_outer_iter_cat<_Base<_Const>>
+ : __detail::__lazy_split_view_outer_iter_cat<_Base<_Const>>
{
private:
- using _Parent = __detail::__maybe_const_t<_Const, split_view>;
- using _Base = split_view::_Base<_Const>;
+ using _Parent = __detail::__maybe_const_t<_Const, lazy_split_view>;
+ using _Base = lazy_split_view::_Base<_Const>;
constexpr bool
__at_end() const
{ return __current() == ranges::end(_M_parent->_M_base); }
- // [range.split.outer] p1
+ // [range.lazy.split.outer] p1
// Many of the following specifications refer to the notional member
// current of outer-iterator. current is equivalent to current_ if
// V models forward_range, and parent_->current_ otherwise.
@@ -2914,7 +2914,7 @@ namespace views::__adaptor
using iterator_concept = conditional_t<forward_range<_Base>,
forward_iterator_tag,
input_iterator_tag>;
- // iterator_category defined in __split_view_outer_iter_cat
+ // iterator_category defined in __lazy_split_view_outer_iter_cat
using difference_type = range_difference_t<_Base>;
struct value_type : view_interface<value_type>
@@ -2974,7 +2974,7 @@ namespace views::__adaptor
operator++()
{
// _GLIBCXX_RESOLVE_LIB_DEFECTS
- // 3505. split_view::outer-iterator::operator++ misspecified
+ // 3505. lazy_split_view::outer-iterator::operator++ misspecified
const auto __end = ranges::end(_M_parent->_M_base);
if (__current() == __end)
return *this;
@@ -3030,10 +3030,10 @@ namespace views::__adaptor
template<bool _Const>
struct _InnerIter
- : __detail::__split_view_inner_iter_cat<_Base<_Const>>
+ : __detail::__lazy_split_view_inner_iter_cat<_Base<_Const>>
{
private:
- using _Base = split_view::_Base<_Const>;
+ using _Base = lazy_split_view::_Base<_Const>;
constexpr bool
__at_end() const
@@ -3081,7 +3081,7 @@ namespace views::__adaptor
public:
using iterator_concept
= typename _OuterIter<_Const>::iterator_concept;
- // iterator_category defined in __split_view_inner_iter_cat
+ // iterator_category defined in __lazy_split_view_inner_iter_cat
using value_type = range_value_t<_Base>;
using difference_type = range_difference_t<_Base>;
@@ -3092,6 +3092,14 @@ namespace views::__adaptor
: _M_i(std::move(__i))
{ }
+ constexpr iterator_t<_Base>
+ base() const& requires copyable<iterator_t<_Base>>
+ { return _M_i_current(); }
+
+ constexpr iterator_t<_Base>
+ base() &&
+ { return std::move(_M_i_current()); }
+
constexpr decltype(auto)
operator*() const
{ return *_M_i_current(); }
@@ -3151,12 +3159,12 @@ namespace views::__adaptor
public:
- split_view() requires (default_initializable<_Vp>
- && default_initializable<_Pattern>)
+ lazy_split_view() requires (default_initializable<_Vp>
+ && default_initializable<_Pattern>)
= default;
constexpr
- split_view(_Vp __base, _Pattern __pattern)
+ lazy_split_view(_Vp __base, _Pattern __pattern)
: _M_pattern(std::move(__pattern)), _M_base(std::move(__base))
{ }
@@ -3164,7 +3172,7 @@ namespace views::__adaptor
requires constructible_from<_Vp, views::all_t<_Range>>
&& constructible_from<_Pattern, single_view<range_value_t<_Range>>>
constexpr
- split_view(_Range&& __r, range_value_t<_Range> __e)
+ lazy_split_view(_Range&& __r, range_value_t<_Range> __e)
: _M_pattern(views::single(std::move(__e))),
_M_base(views::all(std::forward<_Range>(__r)))
{ }
@@ -3216,35 +3224,35 @@ namespace views::__adaptor
};
template<typename _Range, typename _Pattern>
- split_view(_Range&&, _Pattern&&)
- -> split_view<views::all_t<_Range>, views::all_t<_Pattern>>;
+ lazy_split_view(_Range&&, _Pattern&&)
+ -> lazy_split_view<views::all_t<_Range>, views::all_t<_Pattern>>;
template<input_range _Range>
- split_view(_Range&&, range_value_t<_Range>)
- -> split_view<views::all_t<_Range>, single_view<range_value_t<_Range>>>;
+ lazy_split_view(_Range&&, range_value_t<_Range>)
+ -> lazy_split_view<views::all_t<_Range>, single_view<range_value_t<_Range>>>;
namespace views
{
namespace __detail
{
template<typename _Range, typename _Pattern>
- concept __can_split_view
- = requires { split_view(std::declval<_Range>(), std::declval<_Pattern>()); };
+ concept __can_lazy_split_view
+ = requires { lazy_split_view(std::declval<_Range>(), std::declval<_Pattern>()); };
} // namespace __detail
- struct _Split : __adaptor::_RangeAdaptor<_Split>
+ struct _LazySplit : __adaptor::_RangeAdaptor<_LazySplit>
{
template<viewable_range _Range, typename _Pattern>
- requires __detail::__can_split_view<_Range, _Pattern>
+ requires __detail::__can_lazy_split_view<_Range, _Pattern>
constexpr auto
operator()(_Range&& __r, _Pattern&& __f) const
{
- return split_view(std::forward<_Range>(__r), std::forward<_Pattern>(__f));
+ return lazy_split_view(std::forward<_Range>(__r), std::forward<_Pattern>(__f));
}
- using _RangeAdaptor<_Split>::operator();
+ using _RangeAdaptor<_LazySplit>::operator();
static constexpr int _S_arity = 2;
- // The pattern argument of views::split is not always simple -- it can be
+ // The pattern argument of views::lazy_split is not always simple -- it can be
// a non-view range, the value category of which affects whether the call
// is well-formed. But a scalar or a view pattern argument is surely
// simple.
@@ -3254,7 +3262,7 @@ namespace views::__adaptor
&& copy_constructible<_Pattern>);
};
- inline constexpr _Split split;
+ inline constexpr _LazySplit lazy_split;
} // namespace views
namespace views
diff --git a/libstdc++-v3/testsuite/std/ranges/adaptors/100479.cc b/libstdc++-v3/testsuite/std/ranges/adaptors/100479.cc
index ba10b7baf3f..9899ff92c0b 100644
--- a/libstdc++-v3/testsuite/std/ranges/adaptors/100479.cc
+++ b/libstdc++-v3/testsuite/std/ranges/adaptors/100479.cc
@@ -90,7 +90,7 @@ test03()
// Propagating cached iterators during copy/move would cause these asserts
// to fail here.
auto v = views::single(1)
- | views::split(1)
+ | views::lazy_split(1)
| views::drop(0)
| views::drop_while([](auto) { return false; })
| views::filter([](auto) { return true; });
diff --git a/libstdc++-v3/testsuite/std/ranges/adaptors/100577.cc b/libstdc++-v3/testsuite/std/ranges/adaptors/100577.cc
index 06be4980ddb..5ef7f3f59a7 100644
--- a/libstdc++-v3/testsuite/std/ranges/adaptors/100577.cc
+++ b/libstdc++-v3/testsuite/std/ranges/adaptors/100577.cc
@@ -38,11 +38,11 @@ test01()
static_assert(__adaptor_has_simple_extra_args<decltype(views::take), int>);
static_assert(__adaptor_has_simple_extra_args<decltype(views::take_while), identity>);
static_assert(__adaptor_has_simple_extra_args<decltype(views::drop_while), identity>);
- static_assert(__adaptor_has_simple_extra_args<decltype(views::split), std::string_view>);
- static_assert(__adaptor_has_simple_extra_args<decltype(views::split), char>);
- static_assert(!__adaptor_has_simple_extra_args<decltype(views::split), std::string>);
+ static_assert(__adaptor_has_simple_extra_args<decltype(views::lazy_split), std::string_view>);
+ static_assert(__adaptor_has_simple_extra_args<decltype(views::lazy_split), char>);
+ static_assert(!__adaptor_has_simple_extra_args<decltype(views::lazy_split), std::string>);
- // Verify all adaptor closures except for views::split(pattern) have a simple
+ // Verify all adaptor closures except for views::lazy_split(pattern) have a simple
// operator().
using views::__adaptor::__closure_has_simple_call_op;
__closure_has_simple_call_op auto a00 = views::all;
@@ -56,14 +56,14 @@ test01()
__closure_has_simple_call_op auto a08 = views::common;
__closure_has_simple_call_op auto a09 = views::reverse;
__closure_has_simple_call_op auto a10 = views::keys;
- __closure_has_simple_call_op auto a11 = views::split(' ');
+ __closure_has_simple_call_op auto a11 = views::lazy_split(' ');
// Verify composition of simple closures is simple.
__closure_has_simple_call_op auto b
= (a00 | a01) | (a02 | a03) | (a04 | a05 | a06) | (a07 | a08 | a09 | a10) | a11;
- // Verify views::split(non_view_range) is an exception.
+ // Verify views::lazy_split(non_view_range) is an exception.
extern std::string s;
- auto a12 = views::split(s);
+ auto a12 = views::lazy_split(s);
static_assert(!__closure_has_simple_call_op<decltype(a12)>);
static_assert(!__closure_has_simple_call_op<decltype(a12 | a00)>);
static_assert(!__closure_has_simple_call_op<decltype(a00 | a12)>);
@@ -91,9 +91,9 @@ test02()
// implemented using a fallback deleted overload, so when a call is
// ill-formed overload resolution succeeds but selects the deleted overload
// (but only when the closure is invoked as an rvalue).
- views::split(badarg)(x); // { dg-error "deleted function" }
- (views::split(badarg) | views::all)(x); // { dg-error "deleted function" }
- auto a0 = views::split(badarg);
+ views::lazy_split(badarg)(x); // { dg-error "deleted function" }
+ (views::lazy_split(badarg) | views::all)(x); // { dg-error "deleted function" }
+ auto a0 = views::lazy_split(badarg);
a0(x); // { dg-error "no match" };
auto a1 = a0 | views::all;
a1(x); // { dg-error "no match" }
diff --git a/libstdc++-v3/testsuite/std/ranges/adaptors/join.cc b/libstdc++-v3/testsuite/std/ranges/adaptors/join.cc
index d774e8d9385..50af3fdf729 100644
--- a/libstdc++-v3/testsuite/std/ranges/adaptors/join.cc
+++ b/libstdc++-v3/testsuite/std/ranges/adaptors/join.cc
@@ -93,7 +93,7 @@ test05()
{
using namespace std::literals;
std::vector<std::string> x = {"the", " ", "quick", " ", "brown", " ", "fox"};
- auto v = x | views::join | views::split(' ');
+ auto v = x | views::join | views::lazy_split(' ');
auto i = v.begin();
VERIFY( ranges::equal(*i++, "the"sv) );
VERIFY( ranges::equal(*i++, "quick"sv) );
diff --git a/libstdc++-v3/testsuite/std/ranges/adaptors/split.cc b/libstdc++-v3/testsuite/std/ranges/adaptors/lazy_split.cc
similarity index 76%
rename from libstdc++-v3/testsuite/std/ranges/adaptors/split.cc
rename to libstdc++-v3/testsuite/std/ranges/adaptors/lazy_split.cc
index 9d2cfa8632a..12844525d86 100644
--- a/libstdc++-v3/testsuite/std/ranges/adaptors/split.cc
+++ b/libstdc++-v3/testsuite/std/ranges/adaptors/lazy_split.cc
@@ -39,7 +39,7 @@ test01()
{
auto x = "the quick brown fox"sv;
auto p = std::string{" "};
- auto v = x | views::split(views::all(p)); // views::all is needed here after P2281.
+ auto v = x | views::lazy_split(views::all(p)); // views::all is needed here after P2281.
auto i = v.begin();
VERIFY( ranges::equal(*i++, "the"sv) );
VERIFY( ranges::equal(*i++, "quick"sv) );
@@ -52,7 +52,7 @@ void
test02()
{
auto x = "the quick brown fox"sv;
- auto v = x | views::split(' ');
+ auto v = x | views::lazy_split(' ');
auto i = v.begin();
VERIFY( ranges::equal(*i++, "the"sv) );
VERIFY( ranges::equal(*i++, "quick"sv) );
@@ -66,7 +66,7 @@ test03()
{
char x[] = "the quick brown fox";
test_range<char, forward_iterator_wrapper> rx(x, x+sizeof(x)-1);
- auto v = rx | views::split(' ');
+ auto v = rx | views::lazy_split(' ');
auto i = v.begin();
VERIFY( ranges::equal(*i++, "the"sv) );
VERIFY( ranges::equal(*i++, "quick"sv) );
@@ -83,7 +83,7 @@ test04()
static_assert(!ranges::view<decltype(p)>);
static_assert(std::same_as<decltype(p | views::all),
ranges::ref_view<decltype(p)>>);
- auto v = x | views::split(views::all(p)); // views::all is needed here after P2281.
+ auto v = x | views::lazy_split(views::all(p)); // views::all is needed here after P2281.
auto i = v.begin();
VERIFY( ranges::equal(*i++, "the"sv) );
VERIFY( ranges::equal(*i++, "quick"sv) );
@@ -102,7 +102,7 @@ test05()
std::string str
= "Now is the time for all good men to come to the aid of their county.";
auto rng
- = str | views::split(' ') | views::transform(as_string) | views::common;
+ = str | views::lazy_split(' ') | views::transform(as_string) | views::common;
std::vector<std::string> words(rng.begin(), rng.end());
auto not_space_p = [](char c) { return c != ' '; };
VERIFY( ranges::equal(words | views::join,
@@ -113,7 +113,7 @@ void
test06()
{
std::string str = "hello world";
- auto v = str | views::transform(std::identity{}) | views::split(' ');
+ auto v = str | views::transform(std::identity{}) | views::lazy_split(' ');
// Verify that _Iterator<false> is implicitly convertible to _Iterator<true>.
static_assert(!std::same_as<decltype(ranges::begin(v)),
@@ -126,7 +126,7 @@ void
test07()
{
char str[] = "banana split";
- auto split = str | views::split(' ');
+ auto split = str | views::lazy_split(' ');
auto val = *split.begin();
auto b = val.begin();
auto b2 = b++;
@@ -139,7 +139,7 @@ test08()
{
char x[] = "the quick brown fox";
test_range<char, input_iterator_wrapper> rx(x, x+sizeof(x)-1);
- auto v = rx | views::split(' ');
+ auto v = rx | views::lazy_split(' ');
auto i = v.begin();
VERIFY( ranges::equal(*i, "the"sv) );
++i;
@@ -152,32 +152,32 @@ test08()
VERIFY( i == v.end() );
}
-template<auto split = views::split>
+template<auto lazy_split = views::lazy_split>
void
test09()
{
// Verify SFINAE behavior.
std::string s, p;
- static_assert(!requires { split(); });
- static_assert(!requires { split(s, p, 0); });
- static_assert(!requires { split(p)(); });
- static_assert(!requires { s | split; });
-
- static_assert(!requires { s | split(p); });
- static_assert(!requires { split(p)(s); });
- static_assert(!requires { s | (split(p) | views::all); });
- static_assert(!requires { (split(p) | views::all)(s); });
-
- static_assert(requires { s | split(views::all(p)); });
- static_assert(requires { split(views::all(p))(s); });
- static_assert(requires { s | (split(views::all(p)) | views::all); });
- static_assert(requires { (split(views::all(p)) | views::all)(s); });
-
- auto adapt = split(p);
+ static_assert(!requires { lazy_split(); });
+ static_assert(!requires { lazy_split(s, p, 0); });
+ static_assert(!requires { lazy_split(p)(); });
+ static_assert(!requires { s | lazy_split; });
+
+ static_assert(!requires { s | lazy_split(p); });
+ static_assert(!requires { lazy_split(p)(s); });
+ static_assert(!requires { s | (lazy_split(p) | views::all); });
+ static_assert(!requires { (lazy_split(p) | views::all)(s); });
+
+ static_assert(requires { s | lazy_split(views::all(p)); });
+ static_assert(requires { lazy_split(views::all(p))(s); });
+ static_assert(requires { s | (lazy_split(views::all(p)) | views::all); });
+ static_assert(requires { (lazy_split(views::all(p)) | views::all)(s); });
+
+ auto adapt = lazy_split(p);
static_assert(requires { s | adapt; });
static_assert(requires { adapt(s); });
- auto adapt2 = split(p) | views::all;
+ auto adapt2 = lazy_split(p) | views::all;
static_assert(requires { s | adapt2; });
static_assert(requires { adapt2(s); });
}
@@ -189,7 +189,7 @@ test10()
auto to_string = [] (auto r) {
return std::string(r.begin(), ranges::next(r.begin(), r.end()));
};
- auto v = "xxyx"sv | views::split("xy"sv) | views::transform(to_string);
+ auto v = "xxyx"sv | views::lazy_split("xy"sv) | views::transform(to_string);
VERIFY( ranges::equal(v, (std::string_view[]){"x", "x"}) );
}
diff --git a/libstdc++-v3/testsuite/std/ranges/adaptors/split_neg.cc b/libstdc++-v3/testsuite/std/ranges/adaptors/lazy_split_neg.cc
similarity index 79%
rename from libstdc++-v3/testsuite/std/ranges/adaptors/split_neg.cc
rename to libstdc++-v3/testsuite/std/ranges/adaptors/lazy_split_neg.cc
index 4229314a9dc..c59f828ee56 100644
--- a/libstdc++-v3/testsuite/std/ranges/adaptors/split_neg.cc
+++ b/libstdc++-v3/testsuite/std/ranges/adaptors/lazy_split_neg.cc
@@ -30,7 +30,7 @@ test01()
{
using namespace std::literals;
auto x = "the quick brown fox"sv;
- auto v = views::split(x, std::initializer_list<char>{' ', ' '}); // { dg-error "no match" }
+ auto v = views::lazy_split(x, std::initializer_list<char>{' ', ' '}); // { dg-error "no match" }
}
void
@@ -38,8 +38,8 @@ test02()
{
using namespace std::literals;
auto x = "the quick brown fox"sv;
- auto v1 = views::split(std::initializer_list<char>{' ', ' '})(x); // { dg-error "deleted" }
- auto v2 = x | views::split(std::initializer_list<char>{' ', ' '}); // { dg-error "no match" }
+ auto v1 = views::lazy_split(std::initializer_list<char>{' ', ' '})(x); // { dg-error "deleted" }
+ auto v2 = x | views::lazy_split(std::initializer_list<char>{' ', ' '}); // { dg-error "no match" }
}
// { dg-prune-output "in requirements" }
diff --git a/libstdc++-v3/testsuite/std/ranges/adaptors/p2281.cc b/libstdc++-v3/testsuite/std/ranges/adaptors/p2281.cc
index c916a5ea8b7..7950c43576d 100644
--- a/libstdc++-v3/testsuite/std/ranges/adaptors/p2281.cc
+++ b/libstdc++-v3/testsuite/std/ranges/adaptors/p2281.cc
@@ -34,7 +34,7 @@ void
test01()
{
auto split_into_strings = [] (auto p) {
- return views::split(p) | views::transform([](auto r){
+ return views::lazy_split(p) | views::transform([](auto r){
return std::string(r.begin(), ranges::next(r.begin(), r.end()));
});
};
@@ -60,19 +60,19 @@ struct move_only_range
template<>
inline constexpr bool std::ranges::enable_view<move_only_range> = true;
-template<auto split = views::split>
+template<auto lazy_split = views::lazy_split>
void
test02()
{
std::string_view s;
move_only_range p;
- static_assert(requires { s | split(std::move(p)); });
- static_assert(requires { split(std::move(p))(s); });
- static_assert(requires { split(std::move(p)) | views::all; });
- static_assert(requires { views::all | split(std::move(p)); });
- static_assert(!requires { split(p); });
- static_assert(!requires { split(p) | views::all; });
- static_assert(!requires { views::all | split(p); });
+ static_assert(requires { s | lazy_split(std::move(p)); });
+ static_assert(requires { lazy_split(std::move(p))(s); });
+ static_assert(requires { lazy_split(std::move(p)) | views::all; });
+ static_assert(requires { views::all | lazy_split(std::move(p)); });
+ static_assert(!requires { lazy_split(p); });
+ static_assert(!requires { lazy_split(p) | views::all; });
+ static_assert(!requires { views::all | lazy_split(p); });
}
int
diff --git a/libstdc++-v3/testsuite/std/ranges/adaptors/sizeof.cc b/libstdc++-v3/testsuite/std/ranges/adaptors/sizeof.cc
index 80326f8bf21..219e2a61f07 100644
--- a/libstdc++-v3/testsuite/std/ranges/adaptors/sizeof.cc
+++ b/libstdc++-v3/testsuite/std/ranges/adaptors/sizeof.cc
@@ -46,7 +46,7 @@ static_assert(sizeof(ranges::take_while_view<V, decltype(pred_l)>) == 3*ptr);
static_assert(sizeof(ranges::drop_while_view<V, decltype(pred_l)>) == 3*ptr);
static_assert(sizeof(ranges::transform_view<V, decltype(func_l)>) == 3*ptr);
-static_assert(sizeof(ranges::split_view<V, std::string_view>) == 4*ptr);
+static_assert(sizeof(ranges::lazy_split_view<V, std::string_view>) == 4*ptr);
static_assert
(sizeof(ranges::reverse_view<ranges::filter_view<V, decltype(pred_l)>>) == 4*ptr);
diff --git a/libstdc++-v3/testsuite/std/ranges/p2259.cc b/libstdc++-v3/testsuite/std/ranges/p2259.cc
index 1b422e44f16..0ec7e21f657 100644
--- a/libstdc++-v3/testsuite/std/ranges/p2259.cc
+++ b/libstdc++-v3/testsuite/std/ranges/p2259.cc
@@ -49,12 +49,12 @@ test01()
// Verify the changes to transform_view.
only_cxx20_input_range auto v2 = v0 | views::transform([](int& c) -> auto& { return c; });
- // Verify the changes to split_view.
- only_cxx20_input_range auto v3 = v0 | views::split(12);
+ // Verify the changes to lazy_split_view.
+ only_cxx20_input_range auto v3 = v0 | views::lazy_split(12);
static_assert(only_cxx20_input_range<decltype(*v3.begin())>);
// Verify the changes to join_view.
- only_cxx20_input_range auto v4 = v0 | views::split(12) | views::join;
+ only_cxx20_input_range auto v4 = v0 | views::lazy_split(12) | views::join;
// Verify the changes to elements_view.
only_cxx20_input_range auto v5
diff --git a/libstdc++-v3/testsuite/std/ranges/p2325.cc b/libstdc++-v3/testsuite/std/ranges/p2325.cc
index df6cde29e4d..4d075409026 100644
--- a/libstdc++-v3/testsuite/std/ranges/p2325.cc
+++ b/libstdc++-v3/testsuite/std/ranges/p2325.cc
@@ -113,14 +113,14 @@ test07()
void
test08()
{
- // Verify split_view is conditionally default constructible.
- using type1 = ranges::split_view<ranges::ref_view<int[2]>, ranges::single_view<int>>;
+ // Verify lazy_split_view is conditionally default constructible.
+ using type1 = ranges::lazy_split_view<ranges::ref_view<int[2]>, ranges::single_view<int>>;
static_assert(!default_initializable<type1>);
- using type2 = ranges::split_view<ranges::single_view<int>, ranges::ref_view<int[2]>>;
+ using type2 = ranges::lazy_split_view<ranges::single_view<int>, ranges::ref_view<int[2]>>;
static_assert(!default_initializable<type2>);
- using type3 = ranges::split_view<ranges::ref_view<int[2]>, ranges::ref_view<int[2]>>;
+ using type3 = ranges::lazy_split_view<ranges::ref_view<int[2]>, ranges::ref_view<int[2]>>;
static_assert(!default_initializable<type3>);
- using type4 = ranges::split_view<ranges::single_view<int>, ranges::single_view<int>>;
+ using type4 = ranges::lazy_split_view<ranges::single_view<int>, ranges::single_view<int>>;
static_assert(default_initializable<type4>);
}
diff --git a/libstdc++-v3/testsuite/std/ranges/p2367.cc b/libstdc++-v3/testsuite/std/ranges/p2367.cc
index 40875233d88..5228b021602 100644
--- a/libstdc++-v3/testsuite/std/ranges/p2367.cc
+++ b/libstdc++-v3/testsuite/std/ranges/p2367.cc
@@ -43,6 +43,6 @@ test01()
// Verify changes to views::drop.
auto v5 = views::drop(x, 0ull);
- // Verify changes to views::split.
- auto v6 = views::split(x, 5u);
+ // Verify changes to views::lazy_split.
+ auto v6 = views::lazy_split(x, 5u);
}
--
2.32.0.93.g670b81a890
^ permalink raw reply [flat|nested] 11+ messages in thread
* [PATCH 4/5] libstdc++: Implement resolution of LWG 3478 as per P2210
2021-06-17 15:22 [PATCH 1/5] libstdc++: Implement P2325 changes to default-constructibilty of views Patrick Palka
2021-06-17 15:22 ` [PATCH 2/5] libstdc++: Move ranges algos used by <ranges> into ranges_util.h Patrick Palka
2021-06-17 15:22 ` [PATCH 3/5] libstdc++: Rename views::split to views::lazy_split as per P2210 Patrick Palka
@ 2021-06-17 15:22 ` Patrick Palka
2021-06-18 21:57 ` Jonathan Wakely
2021-06-17 15:22 ` [PATCH 5/5] libstdc++: Implement new views::split " Patrick Palka
2021-06-17 18:19 ` [PATCH 1/5] libstdc++: Implement P2325 changes to default-constructibilty of views Jonathan Wakely
4 siblings, 1 reply; 11+ messages in thread
From: Patrick Palka @ 2021-06-17 15:22 UTC (permalink / raw)
To: gcc-patches; +Cc: libstdc++, Patrick Palka
This implements the part of P2210R2 "Superior String Splitting" that
resolves LWG 3478 for split_view (now named lazy_split_view).
libstdc++-v3/ChangeLog:
* include/std/ranges (lazy_split_view::_OuterIter::__at_end):
Check _M_trailing_empty.
(lazy_split_view::_OuterIter::_M_trailing_empty): Define this
data member.
(lazy_split_view::_OuterIter::operator++): Set _M_trailing_empty
appropriately.
(lazy_split_view::_OuterIter::operator==): Compare
_M_trailing_empty.
* testsuite/std/ranges/adaptors/100479.cc (test03): Expect two
split parts instead of one.
* testsuite/std/ranges/adaptors/lazy_split.cc (test11): New test.
---
libstdc++-v3/include/std/ranges | 21 +++++++++++++++----
.../testsuite/std/ranges/adaptors/100479.cc | 6 +++---
.../std/ranges/adaptors/lazy_split.cc | 15 +++++++++++++
3 files changed, 35 insertions(+), 7 deletions(-)
diff --git a/libstdc++-v3/include/std/ranges b/libstdc++-v3/include/std/ranges
index cc1ef112ff1..78562924bee 100644
--- a/libstdc++-v3/include/std/ranges
+++ b/libstdc++-v3/include/std/ranges
@@ -2879,7 +2879,7 @@ namespace views::__adaptor
constexpr bool
__at_end() const
- { return __current() == ranges::end(_M_parent->_M_base); }
+ { return __current() == ranges::end(_M_parent->_M_base) && !_M_trailing_empty; }
// [range.lazy.split.outer] p1
// Many of the following specifications refer to the notional member
@@ -2909,6 +2909,7 @@ namespace views::__adaptor
[[no_unique_address]]
__detail::__maybe_present_t<forward_range<_Vp>,
iterator_t<_Base>> _M_current;
+ bool _M_trailing_empty = false;
public:
using iterator_concept = conditional_t<forward_range<_Base>,
@@ -2977,7 +2978,10 @@ namespace views::__adaptor
// 3505. lazy_split_view::outer-iterator::operator++ misspecified
const auto __end = ranges::end(_M_parent->_M_base);
if (__current() == __end)
- return *this;
+ {
+ _M_trailing_empty = false;
+ return *this;
+ }
const auto [__pbegin, __pend] = subrange{_M_parent->_M_pattern};
if (__pbegin == __pend)
++__current();
@@ -2986,7 +2990,11 @@ namespace views::__adaptor
__current() = ranges::find(std::move(__current()), __end,
*__pbegin);
if (__current() != __end)
- ++__current();
+ {
+ ++__current();
+ if (__current() == __end)
+ _M_trailing_empty = true;
+ }
}
else
do
@@ -2996,6 +3004,8 @@ namespace views::__adaptor
if (__p == __pend)
{
__current() = __b;
+ if (__current() == __end)
+ _M_trailing_empty = true;
break;
}
} while (++__current() != __end);
@@ -3018,7 +3028,10 @@ namespace views::__adaptor
friend constexpr bool
operator==(const _OuterIter& __x, const _OuterIter& __y)
requires forward_range<_Base>
- { return __x._M_current == __y._M_current; }
+ {
+ return __x._M_current == __y._M_current
+ && __x._M_trailing_empty == __y._M_trailing_empty;
+ }
friend constexpr bool
operator==(const _OuterIter& __x, default_sentinel_t)
diff --git a/libstdc++-v3/testsuite/std/ranges/adaptors/100479.cc b/libstdc++-v3/testsuite/std/ranges/adaptors/100479.cc
index 9899ff92c0b..b8c1e6f4f57 100644
--- a/libstdc++-v3/testsuite/std/ranges/adaptors/100479.cc
+++ b/libstdc++-v3/testsuite/std/ranges/adaptors/100479.cc
@@ -95,11 +95,11 @@ test03()
| views::drop_while([](auto) { return false; })
| views::filter([](auto) { return true; });
static_assert(ranges::forward_range<decltype(v)>);
- VERIFY( ranges::next(v.begin()) == v.end() );
+ VERIFY( ranges::distance(v) == 2 );
auto w = v;
- VERIFY( ranges::next(w.begin()) == w.end() );
+ VERIFY( ranges::distance(v) == 2 );
auto z = std::move(w);
- VERIFY( ranges::next(z.begin()) == z.end() );
+ VERIFY( ranges::distance(v) == 2 );
return true;
}
diff --git a/libstdc++-v3/testsuite/std/ranges/adaptors/lazy_split.cc b/libstdc++-v3/testsuite/std/ranges/adaptors/lazy_split.cc
index 12844525d86..133e9a7025b 100644
--- a/libstdc++-v3/testsuite/std/ranges/adaptors/lazy_split.cc
+++ b/libstdc++-v3/testsuite/std/ranges/adaptors/lazy_split.cc
@@ -193,6 +193,20 @@ test10()
VERIFY( ranges::equal(v, (std::string_view[]){"x", "x"}) );
}
+void
+test11()
+{
+ // LWG 3478
+ static_assert(ranges::distance(views::lazy_split("text"sv, "text"sv)) == 2);
+ static_assert(ranges::distance(views::lazy_split(" text "sv, ' ')) == 3);
+ static_assert(ranges::distance(views::lazy_split(" t e x t "sv, ' ')) == 6);
+ static_assert(ranges::distance(views::lazy_split(" text "sv, " "sv)) == 3);
+ static_assert(ranges::distance(views::lazy_split(" text "sv, " "sv)) == 4);
+ static_assert(ranges::distance(views::lazy_split(" text "sv, " "sv)) == 4);
+ static_assert(ranges::distance(views::lazy_split("t"sv, 't')) == 2);
+ static_assert(ranges::distance(views::lazy_split("text"sv, ""sv)) == 4);
+}
+
int
main()
{
@@ -206,4 +220,5 @@ main()
test08();
test09();
test10();
+ test11();
}
--
2.32.0.93.g670b81a890
^ permalink raw reply [flat|nested] 11+ messages in thread
* [PATCH 5/5] libstdc++: Implement new views::split as per P2210
2021-06-17 15:22 [PATCH 1/5] libstdc++: Implement P2325 changes to default-constructibilty of views Patrick Palka
` (2 preceding siblings ...)
2021-06-17 15:22 ` [PATCH 4/5] libstdc++: Implement resolution of LWG 3478 " Patrick Palka
@ 2021-06-17 15:22 ` Patrick Palka
2021-06-18 21:58 ` Jonathan Wakely
2021-06-17 18:19 ` [PATCH 1/5] libstdc++: Implement P2325 changes to default-constructibilty of views Jonathan Wakely
4 siblings, 1 reply; 11+ messages in thread
From: Patrick Palka @ 2021-06-17 15:22 UTC (permalink / raw)
To: gcc-patches; +Cc: libstdc++, Patrick Palka
This implements the new views::split as specified by P2210R2 "Superior
string splitting".
libstdc++-v3/ChangeLog:
* include/std/ranges (__non_propagating_cache::operator bool):
Define.
(split_view): Define as per P2210.
(views::__detail::__can_split_view): Define.
(views::_Split, views::Split): Define.
* testsuite/std/ranges/adaptors/100577.cc (test01, test02):
Test views::split.
* testsuite/std/ranges/adaptors/split.cc: New test.
* testsuite/std/ranges/p2325.cc (test08a): New test.
* testsuite/std/ranges/p2367.cc (test01): Test views::split.
---
libstdc++-v3/include/std/ranges | 205 ++++++++++++++++++
.../testsuite/std/ranges/adaptors/100577.cc | 16 +-
.../testsuite/std/ranges/adaptors/split.cc | 196 +++++++++++++++++
libstdc++-v3/testsuite/std/ranges/p2325.cc | 14 ++
libstdc++-v3/testsuite/std/ranges/p2367.cc | 1 +
5 files changed, 430 insertions(+), 2 deletions(-)
create mode 100644 libstdc++-v3/testsuite/std/ranges/adaptors/split.cc
diff --git a/libstdc++-v3/include/std/ranges b/libstdc++-v3/include/std/ranges
index 78562924bee..42278f128b8 100644
--- a/libstdc++-v3/include/std/ranges
+++ b/libstdc++-v3/include/std/ranges
@@ -1195,6 +1195,10 @@ namespace views::__adaptor
return *this;
}
+ constexpr explicit
+ operator bool() const noexcept
+ { return this->_M_is_engaged(); }
+
constexpr _Tp&
operator*() noexcept
{ return this->_M_get(); }
@@ -3278,6 +3282,207 @@ namespace views::__adaptor
inline constexpr _LazySplit lazy_split;
} // namespace views
+ template<forward_range _Vp, forward_range _Pattern>
+ requires view<_Vp> && view<_Pattern>
+ && indirectly_comparable<iterator_t<_Vp>, iterator_t<_Pattern>,
+ ranges::equal_to>
+ class split_view : public view_interface<split_view<_Vp, _Pattern>>
+ {
+ private:
+ _Pattern _M_pattern = _Pattern();
+ __detail::__non_propagating_cache<subrange<iterator_t<_Vp>>> _M_cached_begin;
+ _Vp _M_base = _Vp();
+
+ struct _Iterator;
+ struct _Sentinel;
+
+ public:
+ split_view() requires (default_initializable<_Vp>
+ && default_initializable<_Pattern>)
+ = default;
+
+ constexpr
+ split_view(_Vp __base, _Pattern __pattern)
+ : _M_pattern(std::move(__pattern)),
+ _M_base(std::move(__base))
+ { }
+
+ template<forward_range _Range>
+ requires constructible_from<_Vp, views::all_t<_Range>>
+ && constructible_from<_Pattern, single_view<range_value_t<_Range>>>
+ constexpr
+ split_view(_Range&& __r, range_value_t<_Range> __e)
+ : _M_pattern(views::single(__e)),
+ _M_base(views::all(std::forward<_Range>(__r)))
+ { }
+
+ constexpr _Vp
+ base() const& requires copyable<_Vp>
+ { return _M_base; }
+
+ constexpr _Vp
+ base() &&
+ { return std::move(_M_base); }
+
+ constexpr _Iterator
+ begin()
+ {
+ if (!_M_cached_begin)
+ _M_cached_begin = _M_find_next(ranges::begin(_M_base));
+ return {this, ranges::begin(_M_base), *_M_cached_begin};
+ }
+
+ constexpr auto
+ end()
+ {
+ if constexpr (common_range<_Vp>)
+ return _Iterator{this, ranges::end(_M_base), {}};
+ else
+ return _Sentinel{this};
+ }
+
+ constexpr subrange<iterator_t<_Vp>>
+ _M_find_next(iterator_t<_Vp> __it)
+ {
+ auto [__b, __e] = ranges::search(subrange(__it, ranges::end(_M_base)), _M_pattern);
+ if (__b != ranges::end(_M_base) && ranges::empty(_M_pattern))
+ {
+ ++__b;
+ ++__e;
+ }
+ return {__b, __e};
+ }
+
+ private:
+ struct _Iterator
+ {
+ private:
+ split_view* _M_parent = nullptr;
+ iterator_t<_Vp> _M_cur = iterator_t<_Vp>();
+ subrange<iterator_t<_Vp>> _M_next = subrange<iterator_t<_Vp>>();
+ bool _M_trailing_empty = false;
+
+ public:
+ using iterator_concept = forward_iterator_tag;
+ using iterator_category = input_iterator_tag;
+ using value_type = subrange<iterator_t<_Vp>>;
+ using difference_type = range_difference_t<_Vp>;
+
+ _Iterator() requires default_initializable<iterator_t<_Vp>> = default;
+
+ constexpr
+ _Iterator(split_view* __parent,
+ iterator_t<_Vp> __current,
+ subrange<iterator_t<_Vp>> __next)
+ : _M_parent(__parent),
+ _M_cur(std::move(__current)),
+ _M_next(std::move(__next))
+ { }
+
+ constexpr iterator_t<_Vp>
+ base() const
+ { return _M_cur; }
+
+ constexpr value_type
+ operator*() const
+ { return {_M_cur, _M_next.begin()}; }
+
+ constexpr _Iterator&
+ operator++()
+ {
+ _M_cur = _M_next.begin();
+ if (_M_cur != ranges::end(_M_parent->_M_base))
+ {
+ _M_cur = _M_next.end();
+ if (_M_cur == ranges::end(_M_parent->_M_base))
+ {
+ _M_trailing_empty = true;
+ _M_next = {_M_cur, _M_cur};
+ }
+ else
+ _M_next = _M_parent->_M_find_next(_M_cur);
+ }
+ else
+ _M_trailing_empty = false;
+ return *this;
+ }
+
+ constexpr _Iterator
+ operator++(int)
+ {
+ auto __tmp = *this;
+ ++*this;
+ return __tmp;
+ }
+
+ friend constexpr bool
+ operator==(const _Iterator& __x, const _Iterator& __y)
+ {
+ return __x._M_cur == __y._M_cur
+ && __x._M_trailing_empty == __y._M_trailing_empty;
+ }
+
+ friend struct _Sentinel;
+ };
+
+ struct _Sentinel
+ {
+ private:
+ sentinel_t<_Vp> _M_end = sentinel_t<_Vp>();
+
+ constexpr bool
+ _M_equal(const _Iterator& __x) const
+ { return __x._M_cur == _M_end && !__x._M_trailing_empty; }
+
+ public:
+ constexpr explicit
+ _Sentinel(split_view* __parent)
+ : _M_end(ranges::end(__parent->_M_base))
+ { }
+
+ friend constexpr bool
+ operator==(const _Iterator& __x, const _Sentinel& __y)
+ { return __y._M_equal(__x); }
+ };
+ };
+
+ template<typename _Range, typename _Pattern>
+ split_view(_Range&&, _Pattern&&)
+ -> split_view<views::all_t<_Range>, views::all_t<_Pattern>>;
+
+ template<forward_range _Range>
+ split_view(_Range&&, range_value_t<_Range>)
+ -> split_view<views::all_t<_Range>, single_view<range_value_t<_Range>>>;
+
+ namespace views
+ {
+ namespace __detail
+ {
+ template<typename _Range, typename _Pattern>
+ concept __can_split_view
+ = requires { split_view(std::declval<_Range>(), std::declval<_Pattern>()); };
+ } // namespace __detail
+
+ struct _Split : __adaptor::_RangeAdaptor<_Split>
+ {
+ template<viewable_range _Range, typename _Pattern>
+ requires __detail::__can_split_view<_Range, _Pattern>
+ constexpr auto
+ operator()(_Range&& __r, _Pattern&& __f) const
+ {
+ return split_view(std::forward<_Range>(__r), std::forward<_Pattern>(__f));
+ }
+
+ using _RangeAdaptor<_Split>::operator();
+ static constexpr int _S_arity = 2;
+ template<typename _Pattern>
+ static constexpr bool _S_has_simple_extra_args
+ = _LazySplit::_S_has_simple_extra_args<_Pattern>;
+ };
+
+ inline constexpr _Split split;
+ } // namespace views
+
namespace views
{
struct _Counted
diff --git a/libstdc++-v3/testsuite/std/ranges/adaptors/100577.cc b/libstdc++-v3/testsuite/std/ranges/adaptors/100577.cc
index 5ef7f3f59a7..81f2a62cfaa 100644
--- a/libstdc++-v3/testsuite/std/ranges/adaptors/100577.cc
+++ b/libstdc++-v3/testsuite/std/ranges/adaptors/100577.cc
@@ -42,8 +42,6 @@ test01()
static_assert(__adaptor_has_simple_extra_args<decltype(views::lazy_split), char>);
static_assert(!__adaptor_has_simple_extra_args<decltype(views::lazy_split), std::string>);
- // Verify all adaptor closures except for views::lazy_split(pattern) have a simple
- // operator().
using views::__adaptor::__closure_has_simple_call_op;
__closure_has_simple_call_op auto a00 = views::all;
__closure_has_simple_call_op auto a01 = views::transform(std::identity{});
@@ -57,6 +55,7 @@ test01()
__closure_has_simple_call_op auto a09 = views::reverse;
__closure_has_simple_call_op auto a10 = views::keys;
__closure_has_simple_call_op auto a11 = views::lazy_split(' ');
+ __closure_has_simple_call_op auto a11a = views::split(' ');
// Verify composition of simple closures is simple.
__closure_has_simple_call_op auto b
= (a00 | a01) | (a02 | a03) | (a04 | a05 | a06) | (a07 | a08 | a09 | a10) | a11;
@@ -67,6 +66,12 @@ test01()
static_assert(!__closure_has_simple_call_op<decltype(a12)>);
static_assert(!__closure_has_simple_call_op<decltype(a12 | a00)>);
static_assert(!__closure_has_simple_call_op<decltype(a00 | a12)>);
+
+ // Likewise views::split(non_view_range).
+ auto a12a = views::split(s);
+ static_assert(!__closure_has_simple_call_op<decltype(a12a)>);
+ static_assert(!__closure_has_simple_call_op<decltype(a12a | a00)>);
+ static_assert(!__closure_has_simple_call_op<decltype(a00 | a12a)>);
}
void
@@ -98,6 +103,13 @@ test02()
auto a1 = a0 | views::all;
a1(x); // { dg-error "no match" }
+ views::lazy_split(badarg)(x); // { dg-error "deleted function" }
+ (views::lazy_split(badarg) | views::all)(x); // { dg-error "deleted function" }
+ auto a0a = views::split(badarg);
+ a0a(x); // { dg-error "no match" };
+ auto a1a = a0a | views::all;
+ a1a(x); // { dg-error "no match" }
+
views::take(badarg)(x); // { dg-error "deleted" }
views::drop(badarg)(x); // { dg-error "deleted" }
(views::take(badarg) | views::all)(x); // { dg-error "deleted" }
diff --git a/libstdc++-v3/testsuite/std/ranges/adaptors/split.cc b/libstdc++-v3/testsuite/std/ranges/adaptors/split.cc
new file mode 100644
index 00000000000..9e6726cd07f
--- /dev/null
+++ b/libstdc++-v3/testsuite/std/ranges/adaptors/split.cc
@@ -0,0 +1,196 @@
+// Copyright (C) 2020-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++2a" }
+// { dg-do run { target c++2a } }
+
+#include <algorithm>
+#include <ranges>
+#include <string>
+#include <string_view>
+#include <testsuite_hooks.h>
+#include <testsuite_iterators.h>
+
+using __gnu_test::test_range;
+using __gnu_test::forward_iterator_wrapper;
+using __gnu_test::input_iterator_wrapper;
+
+namespace ranges = std::ranges;
+namespace views = std::ranges::views;
+
+using namespace std::literals;
+
+void
+test01()
+{
+ auto from_chars = [] (auto v) {
+ return std::stoi(std::string(v.data(), v.data() + v.size()));
+ };
+ auto ints = "1.2.3.4"sv
+ | views::split('.')
+ | views::transform(from_chars);
+ VERIFY( ranges::equal(ints, (int[]){1,2,3,4}) );
+}
+
+// The following testcases were adapted from lazy_split.cc.
+namespace from_lazy_split_cc
+{
+void
+test01()
+{
+ auto x = "the quick brown fox"sv;
+ auto p = std::string{" "};
+ auto v = x | views::split(views::all(p)); // views::all is needed here after P2281.
+ auto i = v.begin();
+ VERIFY( ranges::equal(*i++, "the"sv) );
+ VERIFY( ranges::equal(*i++, "quick"sv) );
+ VERIFY( ranges::equal(*i++, "brown"sv) );
+ VERIFY( ranges::equal(*i++, "fox"sv) );
+ VERIFY( i == v.end() );
+}
+
+void
+test02()
+{
+ auto x = "the quick brown fox"sv;
+ auto v = x | views::split(' ');
+ auto i = v.begin();
+ VERIFY( ranges::equal(*i++, "the"sv) );
+ VERIFY( ranges::equal(*i++, "quick"sv) );
+ VERIFY( ranges::equal(*i++, "brown"sv) );
+ VERIFY( ranges::equal(*i++, "fox"sv) );
+ VERIFY( i == v.end() );
+}
+
+void
+test03()
+{
+ char x[] = "the quick brown fox";
+ test_range<char, forward_iterator_wrapper> rx(x, x+sizeof(x)-1);
+ auto v = rx | views::split(' ');
+ auto i = v.begin();
+ VERIFY( ranges::equal(*i++, "the"sv) );
+ VERIFY( ranges::equal(*i++, "quick"sv) );
+ VERIFY( ranges::equal(*i++, "brown"sv) );
+ VERIFY( ranges::equal(*i++, "fox"sv) );
+ VERIFY( i == v.end() );
+}
+
+void
+test04()
+{
+ auto x = "the quick brown fox"sv;
+ std::initializer_list<char> p = {' ', ' '};
+ static_assert(!ranges::view<decltype(p)>);
+ static_assert(std::same_as<decltype(p | views::all),
+ ranges::ref_view<decltype(p)>>);
+ auto v = x | views::split(views::all(p)); // views::all is needed here after P2281.
+ auto i = v.begin();
+ VERIFY( ranges::equal(*i++, "the"sv) );
+ VERIFY( ranges::equal(*i++, "quick"sv) );
+ VERIFY( ranges::equal(*i++, "brown"sv) );
+ VERIFY( ranges::equal(*i++, "fox"sv) );
+ VERIFY( i == v.end() );
+}
+
+void
+test05()
+{
+ auto as_string = [](ranges::view auto rng) {
+ auto in = rng | views::common;
+ return std::string(in.begin(), in.end());
+ };
+ std::string str
+ = "Now is the time for all good men to come to the aid of their county.";
+ auto rng
+ = str | views::split(' ') | views::transform(as_string) | views::common;
+ std::vector<std::string> words(rng.begin(), rng.end());
+ auto not_space_p = [](char c) { return c != ' '; };
+ VERIFY( ranges::equal(words | views::join,
+ str | views::filter(not_space_p)) );
+}
+
+template<auto split = views::split>
+void
+test06()
+{
+ // Verify SFINAE behavior.
+ std::string s, p;
+ static_assert(!requires { split(); });
+ static_assert(!requires { split(s, p, 0); });
+ static_assert(!requires { split(p)(); });
+ static_assert(!requires { s | split; });
+
+ static_assert(!requires { s | split(p); });
+ static_assert(!requires { split(p)(s); });
+ static_assert(!requires { s | (split(p) | views::all); });
+ static_assert(!requires { (split(p) | views::all)(s); });
+
+ static_assert(requires { s | split(views::all(p)); });
+ static_assert(requires { split(views::all(p))(s); });
+ static_assert(requires { s | (split(views::all(p)) | views::all); });
+ static_assert(requires { (split(views::all(p)) | views::all)(s); });
+
+ auto adapt = split(p);
+ static_assert(requires { s | adapt; });
+ static_assert(requires { adapt(s); });
+
+ auto adapt2 = split(p) | views::all;
+ static_assert(requires { s | adapt2; });
+ static_assert(requires { adapt2(s); });
+}
+
+void
+test10()
+{
+ // LWG 3505
+ auto to_string = [] (auto r) {
+ return std::string(r.begin(), ranges::next(r.begin(), r.end()));
+ };
+ auto v = "xxyx"sv | views::split("xy"sv) | views::transform(to_string);
+ VERIFY( ranges::equal(v, (std::string_view[]){"x", "x"}) );
+}
+
+void
+test11()
+{
+ // LWG 3478
+ static_assert(ranges::distance(views::split("text"sv, "text"sv)) == 2);
+ static_assert(ranges::distance(views::split(" text "sv, ' ')) == 3);
+ static_assert(ranges::distance(views::split(" t e x t "sv, ' ')) == 6);
+ static_assert(ranges::distance(views::split(" text "sv, " "sv)) == 3);
+ static_assert(ranges::distance(views::split(" text "sv, " "sv)) == 4);
+ static_assert(ranges::distance(views::split(" text "sv, " "sv)) == 4);
+ static_assert(ranges::distance(views::split("t"sv, 't')) == 2);
+ static_assert(ranges::distance(views::split("text"sv, ""sv)) == 4);
+}
+} // namespace from_lazy_split_cc
+
+int
+main()
+{
+ test01();
+
+ from_lazy_split_cc::test01();
+ from_lazy_split_cc::test02();
+ from_lazy_split_cc::test03();
+ from_lazy_split_cc::test04();
+ from_lazy_split_cc::test05();
+ from_lazy_split_cc::test06();
+ from_lazy_split_cc::test10();
+ from_lazy_split_cc::test11();
+}
diff --git a/libstdc++-v3/testsuite/std/ranges/p2325.cc b/libstdc++-v3/testsuite/std/ranges/p2325.cc
index 4d075409026..d2ebe9af863 100644
--- a/libstdc++-v3/testsuite/std/ranges/p2325.cc
+++ b/libstdc++-v3/testsuite/std/ranges/p2325.cc
@@ -124,6 +124,20 @@ test08()
static_assert(default_initializable<type4>);
}
+void
+test08a()
+{
+ // Verify split_view is conditionally default constructible.
+ using type1 = ranges::split_view<ranges::ref_view<int[2]>, ranges::single_view<int>>;
+ static_assert(!default_initializable<type1>);
+ using type2 = ranges::split_view<ranges::single_view<int>, ranges::ref_view<int[2]>>;
+ static_assert(!default_initializable<type2>);
+ using type3 = ranges::split_view<ranges::ref_view<int[2]>, ranges::ref_view<int[2]>>;
+ static_assert(!default_initializable<type3>);
+ using type4 = ranges::split_view<ranges::single_view<int>, ranges::single_view<int>>;
+ static_assert(default_initializable<type4>);
+}
+
void
test09()
{
diff --git a/libstdc++-v3/testsuite/std/ranges/p2367.cc b/libstdc++-v3/testsuite/std/ranges/p2367.cc
index 5228b021602..70a0304593f 100644
--- a/libstdc++-v3/testsuite/std/ranges/p2367.cc
+++ b/libstdc++-v3/testsuite/std/ranges/p2367.cc
@@ -45,4 +45,5 @@ test01()
// Verify changes to views::lazy_split.
auto v6 = views::lazy_split(x, 5u);
+ auto v7 = views::split(x, 5u);
}
--
2.32.0.93.g670b81a890
^ permalink raw reply [flat|nested] 11+ messages in thread
* Re: [PATCH 2/5] libstdc++: Move ranges algos used by <ranges> into ranges_util.h
2021-06-17 15:22 ` [PATCH 2/5] libstdc++: Move ranges algos used by <ranges> into ranges_util.h Patrick Palka
@ 2021-06-17 18:14 ` Jonathan Wakely
0 siblings, 0 replies; 11+ messages in thread
From: Jonathan Wakely @ 2021-06-17 18:14 UTC (permalink / raw)
To: Patrick Palka; +Cc: gcc-patches, libstdc++
On Thu, 17 Jun 2021, 16:47 Patrick Palka via Libstdc++, <
libstdc++@gcc.gnu.org> wrote:
> The <ranges> header defines simplified copies of some ranges algorithms
> in order to avoid including the entirety of ranges_algo.h. A subsequent
> patch is going to want to use ranges::search in <ranges> as well, but
> that algorithm is more complicated compared to the other copied ones.
>
> So rather than additionally copying ranges::search into <ranges>, this
> patch splits out all the ranges algos used by <ranges> (including
> ranges::search) from ranges_algo.h to ranges_util.h, and deletes the
> simplified copies in <ranges>. This seems like the best place for
> these algorithms, as ranges_util.h is included only from <ranges> and
> ranges_algo.h.
>
OK, thanks.
^ permalink raw reply [flat|nested] 11+ messages in thread
* Re: [PATCH 1/5] libstdc++: Implement P2325 changes to default-constructibilty of views
2021-06-17 15:22 [PATCH 1/5] libstdc++: Implement P2325 changes to default-constructibilty of views Patrick Palka
` (3 preceding siblings ...)
2021-06-17 15:22 ` [PATCH 5/5] libstdc++: Implement new views::split " Patrick Palka
@ 2021-06-17 18:19 ` Jonathan Wakely
2021-06-17 18:28 ` Jonathan Wakely
4 siblings, 1 reply; 11+ messages in thread
From: Jonathan Wakely @ 2021-06-17 18:19 UTC (permalink / raw)
To: Patrick Palka; +Cc: gcc-patches, libstdc++
On Thu, 17 Jun 2021, 16:50 Patrick Palka via Libstdc++, <
libstdc++@gcc.gnu.org> wrote:
> This implements the wording changes of P2325R3 "Views should not be
> required to be default constructible". Changes are relatively
> straightforward, besides perhaps those to __box (which now stands
> for copyable-box instead of semiregular-box) and __non_propagating_cache.
>
> For __box, this patch implements the recommended practice to also avoid
> std::optional when the boxed type is nothrow_move/copy_constructible.
>
> For __non_propagating_cache, now that it's used by split_view::_M_current,
> we need to add assignment from a value of the underlying type to the
> subset of the std::optional API implemented for the cache (needed by
> split_view::begin()). Hence the new __non_propagating_cache::operator=
> overload.
>
> While we're changing __box, this fixes the undesirable list-init in
> the constuctors of the partial specialization as reported in PR100475
> comment #7.
As I said on IRC, I'm not sure why the defaulted default constructors need
to be constrained (rather than just letting them get deleted) but the patch
is OK.
^ permalink raw reply [flat|nested] 11+ messages in thread
* Re: [PATCH 1/5] libstdc++: Implement P2325 changes to default-constructibilty of views
2021-06-17 18:19 ` [PATCH 1/5] libstdc++: Implement P2325 changes to default-constructibilty of views Jonathan Wakely
@ 2021-06-17 18:28 ` Jonathan Wakely
0 siblings, 0 replies; 11+ messages in thread
From: Jonathan Wakely @ 2021-06-17 18:28 UTC (permalink / raw)
To: Patrick Palka; +Cc: gcc-patches, libstdc++
On Thu, 17 Jun 2021, 19:19 Jonathan Wakely, <jwakely.gcc@gmail.com> wrote:
>
>
> On Thu, 17 Jun 2021, 16:50 Patrick Palka via Libstdc++, <
> libstdc++@gcc.gnu.org> wrote:
>
>> This implements the wording changes of P2325R3 "Views should not be
>> required to be default constructible". Changes are relatively
>> straightforward, besides perhaps those to __box (which now stands
>> for copyable-box instead of semiregular-box) and __non_propagating_cache.
>>
>> For __box, this patch implements the recommended practice to also avoid
>> std::optional when the boxed type is nothrow_move/copy_constructible.
>>
>> For __non_propagating_cache, now that it's used by split_view::_M_current,
>> we need to add assignment from a value of the underlying type to the
>> subset of the std::optional API implemented for the cache (needed by
>> split_view::begin()). Hence the new __non_propagating_cache::operator=
>> overload.
>>
>> While we're changing __box, this fixes the undesirable list-init in
>> the constuctors of the partial specialization as reported in PR100475
>> comment #7.
>
>
>
> As I said on IRC, I'm not sure why the defaulted default constructors need
> to be constrained (rather than just letting them get deleted) but the patch
> is OK.
>
Because the default member initializers are in the immediate context, so
the constructor would look valid to is_default_constructible, and only get
deleted when instantiated. (Thanks to Barry for reminding me of that.)
>
^ permalink raw reply [flat|nested] 11+ messages in thread
* Re: [PATCH 3/5] libstdc++: Rename views::split to views::lazy_split as per P2210
2021-06-17 15:22 ` [PATCH 3/5] libstdc++: Rename views::split to views::lazy_split as per P2210 Patrick Palka
@ 2021-06-18 21:56 ` Jonathan Wakely
0 siblings, 0 replies; 11+ messages in thread
From: Jonathan Wakely @ 2021-06-18 21:56 UTC (permalink / raw)
To: Patrick Palka; +Cc: gcc Patches, libstdc++
On Thu, 17 Jun 2021 at 16:55, Patrick Palka via Libstdc++
<libstdc++@gcc.gnu.org> wrote:
>
> This mostly mechanical patch performs the renaming part of P2210R3
> "Superior string splitting". It also defines _InnerIter::base()
> overloads.
>
> libstdc++-v3/ChangeLog:
>
> * include/std/ranges: Rename views::split to views::lazy_split,
> split_view to lazy_split_view, etc. throughout.
> (lazy_split_view::_InnerIter::base): Define as per P2210.
> * testsuite/std/ranges/*: Likewise.
The base() member could be noexcept, but that can be added later.
OK.
> ---
> libstdc++-v3/include/std/ranges | 68 +++++++++++--------
> .../testsuite/std/ranges/adaptors/100479.cc | 2 +-
> .../testsuite/std/ranges/adaptors/100577.cc | 20 +++---
> .../testsuite/std/ranges/adaptors/join.cc | 2 +-
> .../adaptors/{split.cc => lazy_split.cc} | 54 +++++++--------
> .../{split_neg.cc => lazy_split_neg.cc} | 6 +-
> .../testsuite/std/ranges/adaptors/p2281.cc | 18 ++---
> .../testsuite/std/ranges/adaptors/sizeof.cc | 2 +-
> libstdc++-v3/testsuite/std/ranges/p2259.cc | 6 +-
> libstdc++-v3/testsuite/std/ranges/p2325.cc | 10 +--
> libstdc++-v3/testsuite/std/ranges/p2367.cc | 4 +-
> 11 files changed, 100 insertions(+), 92 deletions(-)
> rename libstdc++-v3/testsuite/std/ranges/adaptors/{split.cc => lazy_split.cc} (76%)
> rename libstdc++-v3/testsuite/std/ranges/adaptors/{split_neg.cc => lazy_split_neg.cc} (79%)
>
> diff --git a/libstdc++-v3/include/std/ranges b/libstdc++-v3/include/std/ranges
> index f93a880ff8a..cc1ef112ff1 100644
> --- a/libstdc++-v3/include/std/ranges
> +++ b/libstdc++-v3/include/std/ranges
> @@ -2826,19 +2826,19 @@ namespace views::__adaptor
> && (remove_reference_t<_Range>::size() <= 1);
>
> template<typename _Base>
> - struct __split_view_outer_iter_cat
> + struct __lazy_split_view_outer_iter_cat
> { };
>
> template<forward_range _Base>
> - struct __split_view_outer_iter_cat<_Base>
> + struct __lazy_split_view_outer_iter_cat<_Base>
> { using iterator_category = input_iterator_tag; };
>
> template<typename _Base>
> - struct __split_view_inner_iter_cat
> + struct __lazy_split_view_inner_iter_cat
> { };
>
> template<forward_range _Base>
> - struct __split_view_inner_iter_cat<_Base>
> + struct __lazy_split_view_inner_iter_cat<_Base>
> {
> private:
> static constexpr auto
> @@ -2860,7 +2860,7 @@ namespace views::__adaptor
> && indirectly_comparable<iterator_t<_Vp>, iterator_t<_Pattern>,
> ranges::equal_to>
> && (forward_range<_Vp> || __detail::__tiny_range<_Pattern>)
> - class split_view : public view_interface<split_view<_Vp, _Pattern>>
> + class lazy_split_view : public view_interface<lazy_split_view<_Vp, _Pattern>>
> {
> private:
> template<bool _Const>
> @@ -2871,17 +2871,17 @@ namespace views::__adaptor
>
> template<bool _Const>
> struct _OuterIter
> - : __detail::__split_view_outer_iter_cat<_Base<_Const>>
> + : __detail::__lazy_split_view_outer_iter_cat<_Base<_Const>>
> {
> private:
> - using _Parent = __detail::__maybe_const_t<_Const, split_view>;
> - using _Base = split_view::_Base<_Const>;
> + using _Parent = __detail::__maybe_const_t<_Const, lazy_split_view>;
> + using _Base = lazy_split_view::_Base<_Const>;
>
> constexpr bool
> __at_end() const
> { return __current() == ranges::end(_M_parent->_M_base); }
>
> - // [range.split.outer] p1
> + // [range.lazy.split.outer] p1
> // Many of the following specifications refer to the notional member
> // current of outer-iterator. current is equivalent to current_ if
> // V models forward_range, and parent_->current_ otherwise.
> @@ -2914,7 +2914,7 @@ namespace views::__adaptor
> using iterator_concept = conditional_t<forward_range<_Base>,
> forward_iterator_tag,
> input_iterator_tag>;
> - // iterator_category defined in __split_view_outer_iter_cat
> + // iterator_category defined in __lazy_split_view_outer_iter_cat
> using difference_type = range_difference_t<_Base>;
>
> struct value_type : view_interface<value_type>
> @@ -2974,7 +2974,7 @@ namespace views::__adaptor
> operator++()
> {
> // _GLIBCXX_RESOLVE_LIB_DEFECTS
> - // 3505. split_view::outer-iterator::operator++ misspecified
> + // 3505. lazy_split_view::outer-iterator::operator++ misspecified
> const auto __end = ranges::end(_M_parent->_M_base);
> if (__current() == __end)
> return *this;
> @@ -3030,10 +3030,10 @@ namespace views::__adaptor
>
> template<bool _Const>
> struct _InnerIter
> - : __detail::__split_view_inner_iter_cat<_Base<_Const>>
> + : __detail::__lazy_split_view_inner_iter_cat<_Base<_Const>>
> {
> private:
> - using _Base = split_view::_Base<_Const>;
> + using _Base = lazy_split_view::_Base<_Const>;
>
> constexpr bool
> __at_end() const
> @@ -3081,7 +3081,7 @@ namespace views::__adaptor
> public:
> using iterator_concept
> = typename _OuterIter<_Const>::iterator_concept;
> - // iterator_category defined in __split_view_inner_iter_cat
> + // iterator_category defined in __lazy_split_view_inner_iter_cat
> using value_type = range_value_t<_Base>;
> using difference_type = range_difference_t<_Base>;
>
> @@ -3092,6 +3092,14 @@ namespace views::__adaptor
> : _M_i(std::move(__i))
> { }
>
> + constexpr iterator_t<_Base>
> + base() const& requires copyable<iterator_t<_Base>>
> + { return _M_i_current(); }
> +
> + constexpr iterator_t<_Base>
> + base() &&
> + { return std::move(_M_i_current()); }
> +
> constexpr decltype(auto)
> operator*() const
> { return *_M_i_current(); }
> @@ -3151,12 +3159,12 @@ namespace views::__adaptor
>
>
> public:
> - split_view() requires (default_initializable<_Vp>
> - && default_initializable<_Pattern>)
> + lazy_split_view() requires (default_initializable<_Vp>
> + && default_initializable<_Pattern>)
> = default;
>
> constexpr
> - split_view(_Vp __base, _Pattern __pattern)
> + lazy_split_view(_Vp __base, _Pattern __pattern)
> : _M_pattern(std::move(__pattern)), _M_base(std::move(__base))
> { }
>
> @@ -3164,7 +3172,7 @@ namespace views::__adaptor
> requires constructible_from<_Vp, views::all_t<_Range>>
> && constructible_from<_Pattern, single_view<range_value_t<_Range>>>
> constexpr
> - split_view(_Range&& __r, range_value_t<_Range> __e)
> + lazy_split_view(_Range&& __r, range_value_t<_Range> __e)
> : _M_pattern(views::single(std::move(__e))),
> _M_base(views::all(std::forward<_Range>(__r)))
> { }
> @@ -3216,35 +3224,35 @@ namespace views::__adaptor
> };
>
> template<typename _Range, typename _Pattern>
> - split_view(_Range&&, _Pattern&&)
> - -> split_view<views::all_t<_Range>, views::all_t<_Pattern>>;
> + lazy_split_view(_Range&&, _Pattern&&)
> + -> lazy_split_view<views::all_t<_Range>, views::all_t<_Pattern>>;
>
> template<input_range _Range>
> - split_view(_Range&&, range_value_t<_Range>)
> - -> split_view<views::all_t<_Range>, single_view<range_value_t<_Range>>>;
> + lazy_split_view(_Range&&, range_value_t<_Range>)
> + -> lazy_split_view<views::all_t<_Range>, single_view<range_value_t<_Range>>>;
>
> namespace views
> {
> namespace __detail
> {
> template<typename _Range, typename _Pattern>
> - concept __can_split_view
> - = requires { split_view(std::declval<_Range>(), std::declval<_Pattern>()); };
> + concept __can_lazy_split_view
> + = requires { lazy_split_view(std::declval<_Range>(), std::declval<_Pattern>()); };
> } // namespace __detail
>
> - struct _Split : __adaptor::_RangeAdaptor<_Split>
> + struct _LazySplit : __adaptor::_RangeAdaptor<_LazySplit>
> {
> template<viewable_range _Range, typename _Pattern>
> - requires __detail::__can_split_view<_Range, _Pattern>
> + requires __detail::__can_lazy_split_view<_Range, _Pattern>
> constexpr auto
> operator()(_Range&& __r, _Pattern&& __f) const
> {
> - return split_view(std::forward<_Range>(__r), std::forward<_Pattern>(__f));
> + return lazy_split_view(std::forward<_Range>(__r), std::forward<_Pattern>(__f));
> }
>
> - using _RangeAdaptor<_Split>::operator();
> + using _RangeAdaptor<_LazySplit>::operator();
> static constexpr int _S_arity = 2;
> - // The pattern argument of views::split is not always simple -- it can be
> + // The pattern argument of views::lazy_split is not always simple -- it can be
> // a non-view range, the value category of which affects whether the call
> // is well-formed. But a scalar or a view pattern argument is surely
> // simple.
> @@ -3254,7 +3262,7 @@ namespace views::__adaptor
> && copy_constructible<_Pattern>);
> };
>
> - inline constexpr _Split split;
> + inline constexpr _LazySplit lazy_split;
> } // namespace views
>
> namespace views
> diff --git a/libstdc++-v3/testsuite/std/ranges/adaptors/100479.cc b/libstdc++-v3/testsuite/std/ranges/adaptors/100479.cc
> index ba10b7baf3f..9899ff92c0b 100644
> --- a/libstdc++-v3/testsuite/std/ranges/adaptors/100479.cc
> +++ b/libstdc++-v3/testsuite/std/ranges/adaptors/100479.cc
> @@ -90,7 +90,7 @@ test03()
> // Propagating cached iterators during copy/move would cause these asserts
> // to fail here.
> auto v = views::single(1)
> - | views::split(1)
> + | views::lazy_split(1)
> | views::drop(0)
> | views::drop_while([](auto) { return false; })
> | views::filter([](auto) { return true; });
> diff --git a/libstdc++-v3/testsuite/std/ranges/adaptors/100577.cc b/libstdc++-v3/testsuite/std/ranges/adaptors/100577.cc
> index 06be4980ddb..5ef7f3f59a7 100644
> --- a/libstdc++-v3/testsuite/std/ranges/adaptors/100577.cc
> +++ b/libstdc++-v3/testsuite/std/ranges/adaptors/100577.cc
> @@ -38,11 +38,11 @@ test01()
> static_assert(__adaptor_has_simple_extra_args<decltype(views::take), int>);
> static_assert(__adaptor_has_simple_extra_args<decltype(views::take_while), identity>);
> static_assert(__adaptor_has_simple_extra_args<decltype(views::drop_while), identity>);
> - static_assert(__adaptor_has_simple_extra_args<decltype(views::split), std::string_view>);
> - static_assert(__adaptor_has_simple_extra_args<decltype(views::split), char>);
> - static_assert(!__adaptor_has_simple_extra_args<decltype(views::split), std::string>);
> + static_assert(__adaptor_has_simple_extra_args<decltype(views::lazy_split), std::string_view>);
> + static_assert(__adaptor_has_simple_extra_args<decltype(views::lazy_split), char>);
> + static_assert(!__adaptor_has_simple_extra_args<decltype(views::lazy_split), std::string>);
>
> - // Verify all adaptor closures except for views::split(pattern) have a simple
> + // Verify all adaptor closures except for views::lazy_split(pattern) have a simple
> // operator().
> using views::__adaptor::__closure_has_simple_call_op;
> __closure_has_simple_call_op auto a00 = views::all;
> @@ -56,14 +56,14 @@ test01()
> __closure_has_simple_call_op auto a08 = views::common;
> __closure_has_simple_call_op auto a09 = views::reverse;
> __closure_has_simple_call_op auto a10 = views::keys;
> - __closure_has_simple_call_op auto a11 = views::split(' ');
> + __closure_has_simple_call_op auto a11 = views::lazy_split(' ');
> // Verify composition of simple closures is simple.
> __closure_has_simple_call_op auto b
> = (a00 | a01) | (a02 | a03) | (a04 | a05 | a06) | (a07 | a08 | a09 | a10) | a11;
>
> - // Verify views::split(non_view_range) is an exception.
> + // Verify views::lazy_split(non_view_range) is an exception.
> extern std::string s;
> - auto a12 = views::split(s);
> + auto a12 = views::lazy_split(s);
> static_assert(!__closure_has_simple_call_op<decltype(a12)>);
> static_assert(!__closure_has_simple_call_op<decltype(a12 | a00)>);
> static_assert(!__closure_has_simple_call_op<decltype(a00 | a12)>);
> @@ -91,9 +91,9 @@ test02()
> // implemented using a fallback deleted overload, so when a call is
> // ill-formed overload resolution succeeds but selects the deleted overload
> // (but only when the closure is invoked as an rvalue).
> - views::split(badarg)(x); // { dg-error "deleted function" }
> - (views::split(badarg) | views::all)(x); // { dg-error "deleted function" }
> - auto a0 = views::split(badarg);
> + views::lazy_split(badarg)(x); // { dg-error "deleted function" }
> + (views::lazy_split(badarg) | views::all)(x); // { dg-error "deleted function" }
> + auto a0 = views::lazy_split(badarg);
> a0(x); // { dg-error "no match" };
> auto a1 = a0 | views::all;
> a1(x); // { dg-error "no match" }
> diff --git a/libstdc++-v3/testsuite/std/ranges/adaptors/join.cc b/libstdc++-v3/testsuite/std/ranges/adaptors/join.cc
> index d774e8d9385..50af3fdf729 100644
> --- a/libstdc++-v3/testsuite/std/ranges/adaptors/join.cc
> +++ b/libstdc++-v3/testsuite/std/ranges/adaptors/join.cc
> @@ -93,7 +93,7 @@ test05()
> {
> using namespace std::literals;
> std::vector<std::string> x = {"the", " ", "quick", " ", "brown", " ", "fox"};
> - auto v = x | views::join | views::split(' ');
> + auto v = x | views::join | views::lazy_split(' ');
> auto i = v.begin();
> VERIFY( ranges::equal(*i++, "the"sv) );
> VERIFY( ranges::equal(*i++, "quick"sv) );
> diff --git a/libstdc++-v3/testsuite/std/ranges/adaptors/split.cc b/libstdc++-v3/testsuite/std/ranges/adaptors/lazy_split.cc
> similarity index 76%
> rename from libstdc++-v3/testsuite/std/ranges/adaptors/split.cc
> rename to libstdc++-v3/testsuite/std/ranges/adaptors/lazy_split.cc
> index 9d2cfa8632a..12844525d86 100644
> --- a/libstdc++-v3/testsuite/std/ranges/adaptors/split.cc
> +++ b/libstdc++-v3/testsuite/std/ranges/adaptors/lazy_split.cc
> @@ -39,7 +39,7 @@ test01()
> {
> auto x = "the quick brown fox"sv;
> auto p = std::string{" "};
> - auto v = x | views::split(views::all(p)); // views::all is needed here after P2281.
> + auto v = x | views::lazy_split(views::all(p)); // views::all is needed here after P2281.
> auto i = v.begin();
> VERIFY( ranges::equal(*i++, "the"sv) );
> VERIFY( ranges::equal(*i++, "quick"sv) );
> @@ -52,7 +52,7 @@ void
> test02()
> {
> auto x = "the quick brown fox"sv;
> - auto v = x | views::split(' ');
> + auto v = x | views::lazy_split(' ');
> auto i = v.begin();
> VERIFY( ranges::equal(*i++, "the"sv) );
> VERIFY( ranges::equal(*i++, "quick"sv) );
> @@ -66,7 +66,7 @@ test03()
> {
> char x[] = "the quick brown fox";
> test_range<char, forward_iterator_wrapper> rx(x, x+sizeof(x)-1);
> - auto v = rx | views::split(' ');
> + auto v = rx | views::lazy_split(' ');
> auto i = v.begin();
> VERIFY( ranges::equal(*i++, "the"sv) );
> VERIFY( ranges::equal(*i++, "quick"sv) );
> @@ -83,7 +83,7 @@ test04()
> static_assert(!ranges::view<decltype(p)>);
> static_assert(std::same_as<decltype(p | views::all),
> ranges::ref_view<decltype(p)>>);
> - auto v = x | views::split(views::all(p)); // views::all is needed here after P2281.
> + auto v = x | views::lazy_split(views::all(p)); // views::all is needed here after P2281.
> auto i = v.begin();
> VERIFY( ranges::equal(*i++, "the"sv) );
> VERIFY( ranges::equal(*i++, "quick"sv) );
> @@ -102,7 +102,7 @@ test05()
> std::string str
> = "Now is the time for all good men to come to the aid of their county.";
> auto rng
> - = str | views::split(' ') | views::transform(as_string) | views::common;
> + = str | views::lazy_split(' ') | views::transform(as_string) | views::common;
> std::vector<std::string> words(rng.begin(), rng.end());
> auto not_space_p = [](char c) { return c != ' '; };
> VERIFY( ranges::equal(words | views::join,
> @@ -113,7 +113,7 @@ void
> test06()
> {
> std::string str = "hello world";
> - auto v = str | views::transform(std::identity{}) | views::split(' ');
> + auto v = str | views::transform(std::identity{}) | views::lazy_split(' ');
>
> // Verify that _Iterator<false> is implicitly convertible to _Iterator<true>.
> static_assert(!std::same_as<decltype(ranges::begin(v)),
> @@ -126,7 +126,7 @@ void
> test07()
> {
> char str[] = "banana split";
> - auto split = str | views::split(' ');
> + auto split = str | views::lazy_split(' ');
> auto val = *split.begin();
> auto b = val.begin();
> auto b2 = b++;
> @@ -139,7 +139,7 @@ test08()
> {
> char x[] = "the quick brown fox";
> test_range<char, input_iterator_wrapper> rx(x, x+sizeof(x)-1);
> - auto v = rx | views::split(' ');
> + auto v = rx | views::lazy_split(' ');
> auto i = v.begin();
> VERIFY( ranges::equal(*i, "the"sv) );
> ++i;
> @@ -152,32 +152,32 @@ test08()
> VERIFY( i == v.end() );
> }
>
> -template<auto split = views::split>
> +template<auto lazy_split = views::lazy_split>
> void
> test09()
> {
> // Verify SFINAE behavior.
> std::string s, p;
> - static_assert(!requires { split(); });
> - static_assert(!requires { split(s, p, 0); });
> - static_assert(!requires { split(p)(); });
> - static_assert(!requires { s | split; });
> -
> - static_assert(!requires { s | split(p); });
> - static_assert(!requires { split(p)(s); });
> - static_assert(!requires { s | (split(p) | views::all); });
> - static_assert(!requires { (split(p) | views::all)(s); });
> -
> - static_assert(requires { s | split(views::all(p)); });
> - static_assert(requires { split(views::all(p))(s); });
> - static_assert(requires { s | (split(views::all(p)) | views::all); });
> - static_assert(requires { (split(views::all(p)) | views::all)(s); });
> -
> - auto adapt = split(p);
> + static_assert(!requires { lazy_split(); });
> + static_assert(!requires { lazy_split(s, p, 0); });
> + static_assert(!requires { lazy_split(p)(); });
> + static_assert(!requires { s | lazy_split; });
> +
> + static_assert(!requires { s | lazy_split(p); });
> + static_assert(!requires { lazy_split(p)(s); });
> + static_assert(!requires { s | (lazy_split(p) | views::all); });
> + static_assert(!requires { (lazy_split(p) | views::all)(s); });
> +
> + static_assert(requires { s | lazy_split(views::all(p)); });
> + static_assert(requires { lazy_split(views::all(p))(s); });
> + static_assert(requires { s | (lazy_split(views::all(p)) | views::all); });
> + static_assert(requires { (lazy_split(views::all(p)) | views::all)(s); });
> +
> + auto adapt = lazy_split(p);
> static_assert(requires { s | adapt; });
> static_assert(requires { adapt(s); });
>
> - auto adapt2 = split(p) | views::all;
> + auto adapt2 = lazy_split(p) | views::all;
> static_assert(requires { s | adapt2; });
> static_assert(requires { adapt2(s); });
> }
> @@ -189,7 +189,7 @@ test10()
> auto to_string = [] (auto r) {
> return std::string(r.begin(), ranges::next(r.begin(), r.end()));
> };
> - auto v = "xxyx"sv | views::split("xy"sv) | views::transform(to_string);
> + auto v = "xxyx"sv | views::lazy_split("xy"sv) | views::transform(to_string);
> VERIFY( ranges::equal(v, (std::string_view[]){"x", "x"}) );
> }
>
> diff --git a/libstdc++-v3/testsuite/std/ranges/adaptors/split_neg.cc b/libstdc++-v3/testsuite/std/ranges/adaptors/lazy_split_neg.cc
> similarity index 79%
> rename from libstdc++-v3/testsuite/std/ranges/adaptors/split_neg.cc
> rename to libstdc++-v3/testsuite/std/ranges/adaptors/lazy_split_neg.cc
> index 4229314a9dc..c59f828ee56 100644
> --- a/libstdc++-v3/testsuite/std/ranges/adaptors/split_neg.cc
> +++ b/libstdc++-v3/testsuite/std/ranges/adaptors/lazy_split_neg.cc
> @@ -30,7 +30,7 @@ test01()
> {
> using namespace std::literals;
> auto x = "the quick brown fox"sv;
> - auto v = views::split(x, std::initializer_list<char>{' ', ' '}); // { dg-error "no match" }
> + auto v = views::lazy_split(x, std::initializer_list<char>{' ', ' '}); // { dg-error "no match" }
> }
>
> void
> @@ -38,8 +38,8 @@ test02()
> {
> using namespace std::literals;
> auto x = "the quick brown fox"sv;
> - auto v1 = views::split(std::initializer_list<char>{' ', ' '})(x); // { dg-error "deleted" }
> - auto v2 = x | views::split(std::initializer_list<char>{' ', ' '}); // { dg-error "no match" }
> + auto v1 = views::lazy_split(std::initializer_list<char>{' ', ' '})(x); // { dg-error "deleted" }
> + auto v2 = x | views::lazy_split(std::initializer_list<char>{' ', ' '}); // { dg-error "no match" }
> }
>
> // { dg-prune-output "in requirements" }
> diff --git a/libstdc++-v3/testsuite/std/ranges/adaptors/p2281.cc b/libstdc++-v3/testsuite/std/ranges/adaptors/p2281.cc
> index c916a5ea8b7..7950c43576d 100644
> --- a/libstdc++-v3/testsuite/std/ranges/adaptors/p2281.cc
> +++ b/libstdc++-v3/testsuite/std/ranges/adaptors/p2281.cc
> @@ -34,7 +34,7 @@ void
> test01()
> {
> auto split_into_strings = [] (auto p) {
> - return views::split(p) | views::transform([](auto r){
> + return views::lazy_split(p) | views::transform([](auto r){
> return std::string(r.begin(), ranges::next(r.begin(), r.end()));
> });
> };
> @@ -60,19 +60,19 @@ struct move_only_range
> template<>
> inline constexpr bool std::ranges::enable_view<move_only_range> = true;
>
> -template<auto split = views::split>
> +template<auto lazy_split = views::lazy_split>
> void
> test02()
> {
> std::string_view s;
> move_only_range p;
> - static_assert(requires { s | split(std::move(p)); });
> - static_assert(requires { split(std::move(p))(s); });
> - static_assert(requires { split(std::move(p)) | views::all; });
> - static_assert(requires { views::all | split(std::move(p)); });
> - static_assert(!requires { split(p); });
> - static_assert(!requires { split(p) | views::all; });
> - static_assert(!requires { views::all | split(p); });
> + static_assert(requires { s | lazy_split(std::move(p)); });
> + static_assert(requires { lazy_split(std::move(p))(s); });
> + static_assert(requires { lazy_split(std::move(p)) | views::all; });
> + static_assert(requires { views::all | lazy_split(std::move(p)); });
> + static_assert(!requires { lazy_split(p); });
> + static_assert(!requires { lazy_split(p) | views::all; });
> + static_assert(!requires { views::all | lazy_split(p); });
> }
>
> int
> diff --git a/libstdc++-v3/testsuite/std/ranges/adaptors/sizeof.cc b/libstdc++-v3/testsuite/std/ranges/adaptors/sizeof.cc
> index 80326f8bf21..219e2a61f07 100644
> --- a/libstdc++-v3/testsuite/std/ranges/adaptors/sizeof.cc
> +++ b/libstdc++-v3/testsuite/std/ranges/adaptors/sizeof.cc
> @@ -46,7 +46,7 @@ static_assert(sizeof(ranges::take_while_view<V, decltype(pred_l)>) == 3*ptr);
> static_assert(sizeof(ranges::drop_while_view<V, decltype(pred_l)>) == 3*ptr);
> static_assert(sizeof(ranges::transform_view<V, decltype(func_l)>) == 3*ptr);
>
> -static_assert(sizeof(ranges::split_view<V, std::string_view>) == 4*ptr);
> +static_assert(sizeof(ranges::lazy_split_view<V, std::string_view>) == 4*ptr);
>
> static_assert
> (sizeof(ranges::reverse_view<ranges::filter_view<V, decltype(pred_l)>>) == 4*ptr);
> diff --git a/libstdc++-v3/testsuite/std/ranges/p2259.cc b/libstdc++-v3/testsuite/std/ranges/p2259.cc
> index 1b422e44f16..0ec7e21f657 100644
> --- a/libstdc++-v3/testsuite/std/ranges/p2259.cc
> +++ b/libstdc++-v3/testsuite/std/ranges/p2259.cc
> @@ -49,12 +49,12 @@ test01()
> // Verify the changes to transform_view.
> only_cxx20_input_range auto v2 = v0 | views::transform([](int& c) -> auto& { return c; });
>
> - // Verify the changes to split_view.
> - only_cxx20_input_range auto v3 = v0 | views::split(12);
> + // Verify the changes to lazy_split_view.
> + only_cxx20_input_range auto v3 = v0 | views::lazy_split(12);
> static_assert(only_cxx20_input_range<decltype(*v3.begin())>);
>
> // Verify the changes to join_view.
> - only_cxx20_input_range auto v4 = v0 | views::split(12) | views::join;
> + only_cxx20_input_range auto v4 = v0 | views::lazy_split(12) | views::join;
>
> // Verify the changes to elements_view.
> only_cxx20_input_range auto v5
> diff --git a/libstdc++-v3/testsuite/std/ranges/p2325.cc b/libstdc++-v3/testsuite/std/ranges/p2325.cc
> index df6cde29e4d..4d075409026 100644
> --- a/libstdc++-v3/testsuite/std/ranges/p2325.cc
> +++ b/libstdc++-v3/testsuite/std/ranges/p2325.cc
> @@ -113,14 +113,14 @@ test07()
> void
> test08()
> {
> - // Verify split_view is conditionally default constructible.
> - using type1 = ranges::split_view<ranges::ref_view<int[2]>, ranges::single_view<int>>;
> + // Verify lazy_split_view is conditionally default constructible.
> + using type1 = ranges::lazy_split_view<ranges::ref_view<int[2]>, ranges::single_view<int>>;
> static_assert(!default_initializable<type1>);
> - using type2 = ranges::split_view<ranges::single_view<int>, ranges::ref_view<int[2]>>;
> + using type2 = ranges::lazy_split_view<ranges::single_view<int>, ranges::ref_view<int[2]>>;
> static_assert(!default_initializable<type2>);
> - using type3 = ranges::split_view<ranges::ref_view<int[2]>, ranges::ref_view<int[2]>>;
> + using type3 = ranges::lazy_split_view<ranges::ref_view<int[2]>, ranges::ref_view<int[2]>>;
> static_assert(!default_initializable<type3>);
> - using type4 = ranges::split_view<ranges::single_view<int>, ranges::single_view<int>>;
> + using type4 = ranges::lazy_split_view<ranges::single_view<int>, ranges::single_view<int>>;
> static_assert(default_initializable<type4>);
> }
>
> diff --git a/libstdc++-v3/testsuite/std/ranges/p2367.cc b/libstdc++-v3/testsuite/std/ranges/p2367.cc
> index 40875233d88..5228b021602 100644
> --- a/libstdc++-v3/testsuite/std/ranges/p2367.cc
> +++ b/libstdc++-v3/testsuite/std/ranges/p2367.cc
> @@ -43,6 +43,6 @@ test01()
> // Verify changes to views::drop.
> auto v5 = views::drop(x, 0ull);
>
> - // Verify changes to views::split.
> - auto v6 = views::split(x, 5u);
> + // Verify changes to views::lazy_split.
> + auto v6 = views::lazy_split(x, 5u);
> }
> --
> 2.32.0.93.g670b81a890
>
^ permalink raw reply [flat|nested] 11+ messages in thread
* Re: [PATCH 4/5] libstdc++: Implement resolution of LWG 3478 as per P2210
2021-06-17 15:22 ` [PATCH 4/5] libstdc++: Implement resolution of LWG 3478 " Patrick Palka
@ 2021-06-18 21:57 ` Jonathan Wakely
0 siblings, 0 replies; 11+ messages in thread
From: Jonathan Wakely @ 2021-06-18 21:57 UTC (permalink / raw)
To: Patrick Palka; +Cc: gcc Patches, libstdc++
On Thu, 17 Jun 2021 at 16:51, Patrick Palka via Libstdc++
<libstdc++@gcc.gnu.org> wrote:
>
> This implements the part of P2210R2 "Superior String Splitting" that
> resolves LWG 3478 for split_view (now named lazy_split_view).
>
> libstdc++-v3/ChangeLog:
>
> * include/std/ranges (lazy_split_view::_OuterIter::__at_end):
> Check _M_trailing_empty.
> (lazy_split_view::_OuterIter::_M_trailing_empty): Define this
> data member.
> (lazy_split_view::_OuterIter::operator++): Set _M_trailing_empty
> appropriately.
> (lazy_split_view::_OuterIter::operator==): Compare
> _M_trailing_empty.
> * testsuite/std/ranges/adaptors/100479.cc (test03): Expect two
> split parts instead of one.
> * testsuite/std/ranges/adaptors/lazy_split.cc (test11): New test.
OK (for trunk only, I think).
> ---
> libstdc++-v3/include/std/ranges | 21 +++++++++++++++----
> .../testsuite/std/ranges/adaptors/100479.cc | 6 +++---
> .../std/ranges/adaptors/lazy_split.cc | 15 +++++++++++++
> 3 files changed, 35 insertions(+), 7 deletions(-)
>
> diff --git a/libstdc++-v3/include/std/ranges b/libstdc++-v3/include/std/ranges
> index cc1ef112ff1..78562924bee 100644
> --- a/libstdc++-v3/include/std/ranges
> +++ b/libstdc++-v3/include/std/ranges
> @@ -2879,7 +2879,7 @@ namespace views::__adaptor
>
> constexpr bool
> __at_end() const
> - { return __current() == ranges::end(_M_parent->_M_base); }
> + { return __current() == ranges::end(_M_parent->_M_base) && !_M_trailing_empty; }
>
> // [range.lazy.split.outer] p1
> // Many of the following specifications refer to the notional member
> @@ -2909,6 +2909,7 @@ namespace views::__adaptor
> [[no_unique_address]]
> __detail::__maybe_present_t<forward_range<_Vp>,
> iterator_t<_Base>> _M_current;
> + bool _M_trailing_empty = false;
>
> public:
> using iterator_concept = conditional_t<forward_range<_Base>,
> @@ -2977,7 +2978,10 @@ namespace views::__adaptor
> // 3505. lazy_split_view::outer-iterator::operator++ misspecified
> const auto __end = ranges::end(_M_parent->_M_base);
> if (__current() == __end)
> - return *this;
> + {
> + _M_trailing_empty = false;
> + return *this;
> + }
> const auto [__pbegin, __pend] = subrange{_M_parent->_M_pattern};
> if (__pbegin == __pend)
> ++__current();
> @@ -2986,7 +2990,11 @@ namespace views::__adaptor
> __current() = ranges::find(std::move(__current()), __end,
> *__pbegin);
> if (__current() != __end)
> - ++__current();
> + {
> + ++__current();
> + if (__current() == __end)
> + _M_trailing_empty = true;
> + }
> }
> else
> do
> @@ -2996,6 +3004,8 @@ namespace views::__adaptor
> if (__p == __pend)
> {
> __current() = __b;
> + if (__current() == __end)
> + _M_trailing_empty = true;
> break;
> }
> } while (++__current() != __end);
> @@ -3018,7 +3028,10 @@ namespace views::__adaptor
> friend constexpr bool
> operator==(const _OuterIter& __x, const _OuterIter& __y)
> requires forward_range<_Base>
> - { return __x._M_current == __y._M_current; }
> + {
> + return __x._M_current == __y._M_current
> + && __x._M_trailing_empty == __y._M_trailing_empty;
> + }
>
> friend constexpr bool
> operator==(const _OuterIter& __x, default_sentinel_t)
> diff --git a/libstdc++-v3/testsuite/std/ranges/adaptors/100479.cc b/libstdc++-v3/testsuite/std/ranges/adaptors/100479.cc
> index 9899ff92c0b..b8c1e6f4f57 100644
> --- a/libstdc++-v3/testsuite/std/ranges/adaptors/100479.cc
> +++ b/libstdc++-v3/testsuite/std/ranges/adaptors/100479.cc
> @@ -95,11 +95,11 @@ test03()
> | views::drop_while([](auto) { return false; })
> | views::filter([](auto) { return true; });
> static_assert(ranges::forward_range<decltype(v)>);
> - VERIFY( ranges::next(v.begin()) == v.end() );
> + VERIFY( ranges::distance(v) == 2 );
> auto w = v;
> - VERIFY( ranges::next(w.begin()) == w.end() );
> + VERIFY( ranges::distance(v) == 2 );
> auto z = std::move(w);
> - VERIFY( ranges::next(z.begin()) == z.end() );
> + VERIFY( ranges::distance(v) == 2 );
> return true;
> }
>
> diff --git a/libstdc++-v3/testsuite/std/ranges/adaptors/lazy_split.cc b/libstdc++-v3/testsuite/std/ranges/adaptors/lazy_split.cc
> index 12844525d86..133e9a7025b 100644
> --- a/libstdc++-v3/testsuite/std/ranges/adaptors/lazy_split.cc
> +++ b/libstdc++-v3/testsuite/std/ranges/adaptors/lazy_split.cc
> @@ -193,6 +193,20 @@ test10()
> VERIFY( ranges::equal(v, (std::string_view[]){"x", "x"}) );
> }
>
> +void
> +test11()
> +{
> + // LWG 3478
> + static_assert(ranges::distance(views::lazy_split("text"sv, "text"sv)) == 2);
> + static_assert(ranges::distance(views::lazy_split(" text "sv, ' ')) == 3);
> + static_assert(ranges::distance(views::lazy_split(" t e x t "sv, ' ')) == 6);
> + static_assert(ranges::distance(views::lazy_split(" text "sv, " "sv)) == 3);
> + static_assert(ranges::distance(views::lazy_split(" text "sv, " "sv)) == 4);
> + static_assert(ranges::distance(views::lazy_split(" text "sv, " "sv)) == 4);
> + static_assert(ranges::distance(views::lazy_split("t"sv, 't')) == 2);
> + static_assert(ranges::distance(views::lazy_split("text"sv, ""sv)) == 4);
> +}
> +
> int
> main()
> {
> @@ -206,4 +220,5 @@ main()
> test08();
> test09();
> test10();
> + test11();
> }
> --
> 2.32.0.93.g670b81a890
>
^ permalink raw reply [flat|nested] 11+ messages in thread
* Re: [PATCH 5/5] libstdc++: Implement new views::split as per P2210
2021-06-17 15:22 ` [PATCH 5/5] libstdc++: Implement new views::split " Patrick Palka
@ 2021-06-18 21:58 ` Jonathan Wakely
0 siblings, 0 replies; 11+ messages in thread
From: Jonathan Wakely @ 2021-06-18 21:58 UTC (permalink / raw)
To: Patrick Palka; +Cc: gcc Patches, libstdc++
On Thu, 17 Jun 2021 at 16:59, Patrick Palka via Libstdc++
<libstdc++@gcc.gnu.org> wrote:
>
> This implements the new views::split as specified by P2210R2 "Superior
> string splitting".
>
> libstdc++-v3/ChangeLog:
>
> * include/std/ranges (__non_propagating_cache::operator bool):
> Define.
> (split_view): Define as per P2210.
> (views::__detail::__can_split_view): Define.
> (views::_Split, views::Split): Define.
> * testsuite/std/ranges/adaptors/100577.cc (test01, test02):
> Test views::split.
> * testsuite/std/ranges/adaptors/split.cc: New test.
> * testsuite/std/ranges/p2325.cc (test08a): New test.
> * testsuite/std/ranges/p2367.cc (test01): Test views::split.
OK
> ---
> libstdc++-v3/include/std/ranges | 205 ++++++++++++++++++
> .../testsuite/std/ranges/adaptors/100577.cc | 16 +-
> .../testsuite/std/ranges/adaptors/split.cc | 196 +++++++++++++++++
> libstdc++-v3/testsuite/std/ranges/p2325.cc | 14 ++
> libstdc++-v3/testsuite/std/ranges/p2367.cc | 1 +
> 5 files changed, 430 insertions(+), 2 deletions(-)
> create mode 100644 libstdc++-v3/testsuite/std/ranges/adaptors/split.cc
>
> diff --git a/libstdc++-v3/include/std/ranges b/libstdc++-v3/include/std/ranges
> index 78562924bee..42278f128b8 100644
> --- a/libstdc++-v3/include/std/ranges
> +++ b/libstdc++-v3/include/std/ranges
> @@ -1195,6 +1195,10 @@ namespace views::__adaptor
> return *this;
> }
>
> + constexpr explicit
> + operator bool() const noexcept
> + { return this->_M_is_engaged(); }
> +
> constexpr _Tp&
> operator*() noexcept
> { return this->_M_get(); }
> @@ -3278,6 +3282,207 @@ namespace views::__adaptor
> inline constexpr _LazySplit lazy_split;
> } // namespace views
>
> + template<forward_range _Vp, forward_range _Pattern>
> + requires view<_Vp> && view<_Pattern>
> + && indirectly_comparable<iterator_t<_Vp>, iterator_t<_Pattern>,
> + ranges::equal_to>
> + class split_view : public view_interface<split_view<_Vp, _Pattern>>
> + {
> + private:
> + _Pattern _M_pattern = _Pattern();
> + __detail::__non_propagating_cache<subrange<iterator_t<_Vp>>> _M_cached_begin;
> + _Vp _M_base = _Vp();
> +
> + struct _Iterator;
> + struct _Sentinel;
> +
> + public:
> + split_view() requires (default_initializable<_Vp>
> + && default_initializable<_Pattern>)
> + = default;
> +
> + constexpr
> + split_view(_Vp __base, _Pattern __pattern)
> + : _M_pattern(std::move(__pattern)),
> + _M_base(std::move(__base))
> + { }
> +
> + template<forward_range _Range>
> + requires constructible_from<_Vp, views::all_t<_Range>>
> + && constructible_from<_Pattern, single_view<range_value_t<_Range>>>
> + constexpr
> + split_view(_Range&& __r, range_value_t<_Range> __e)
> + : _M_pattern(views::single(__e)),
> + _M_base(views::all(std::forward<_Range>(__r)))
> + { }
> +
> + constexpr _Vp
> + base() const& requires copyable<_Vp>
> + { return _M_base; }
> +
> + constexpr _Vp
> + base() &&
> + { return std::move(_M_base); }
> +
> + constexpr _Iterator
> + begin()
> + {
> + if (!_M_cached_begin)
> + _M_cached_begin = _M_find_next(ranges::begin(_M_base));
> + return {this, ranges::begin(_M_base), *_M_cached_begin};
> + }
> +
> + constexpr auto
> + end()
> + {
> + if constexpr (common_range<_Vp>)
> + return _Iterator{this, ranges::end(_M_base), {}};
> + else
> + return _Sentinel{this};
> + }
> +
> + constexpr subrange<iterator_t<_Vp>>
> + _M_find_next(iterator_t<_Vp> __it)
> + {
> + auto [__b, __e] = ranges::search(subrange(__it, ranges::end(_M_base)), _M_pattern);
> + if (__b != ranges::end(_M_base) && ranges::empty(_M_pattern))
> + {
> + ++__b;
> + ++__e;
> + }
> + return {__b, __e};
> + }
> +
> + private:
> + struct _Iterator
> + {
> + private:
> + split_view* _M_parent = nullptr;
> + iterator_t<_Vp> _M_cur = iterator_t<_Vp>();
> + subrange<iterator_t<_Vp>> _M_next = subrange<iterator_t<_Vp>>();
> + bool _M_trailing_empty = false;
> +
> + public:
> + using iterator_concept = forward_iterator_tag;
> + using iterator_category = input_iterator_tag;
> + using value_type = subrange<iterator_t<_Vp>>;
> + using difference_type = range_difference_t<_Vp>;
> +
> + _Iterator() requires default_initializable<iterator_t<_Vp>> = default;
> +
> + constexpr
> + _Iterator(split_view* __parent,
> + iterator_t<_Vp> __current,
> + subrange<iterator_t<_Vp>> __next)
> + : _M_parent(__parent),
> + _M_cur(std::move(__current)),
> + _M_next(std::move(__next))
> + { }
> +
> + constexpr iterator_t<_Vp>
> + base() const
> + { return _M_cur; }
> +
> + constexpr value_type
> + operator*() const
> + { return {_M_cur, _M_next.begin()}; }
> +
> + constexpr _Iterator&
> + operator++()
> + {
> + _M_cur = _M_next.begin();
> + if (_M_cur != ranges::end(_M_parent->_M_base))
> + {
> + _M_cur = _M_next.end();
> + if (_M_cur == ranges::end(_M_parent->_M_base))
> + {
> + _M_trailing_empty = true;
> + _M_next = {_M_cur, _M_cur};
> + }
> + else
> + _M_next = _M_parent->_M_find_next(_M_cur);
> + }
> + else
> + _M_trailing_empty = false;
> + return *this;
> + }
> +
> + constexpr _Iterator
> + operator++(int)
> + {
> + auto __tmp = *this;
> + ++*this;
> + return __tmp;
> + }
> +
> + friend constexpr bool
> + operator==(const _Iterator& __x, const _Iterator& __y)
> + {
> + return __x._M_cur == __y._M_cur
> + && __x._M_trailing_empty == __y._M_trailing_empty;
> + }
> +
> + friend struct _Sentinel;
> + };
> +
> + struct _Sentinel
> + {
> + private:
> + sentinel_t<_Vp> _M_end = sentinel_t<_Vp>();
> +
> + constexpr bool
> + _M_equal(const _Iterator& __x) const
> + { return __x._M_cur == _M_end && !__x._M_trailing_empty; }
> +
> + public:
> + constexpr explicit
> + _Sentinel(split_view* __parent)
> + : _M_end(ranges::end(__parent->_M_base))
> + { }
> +
> + friend constexpr bool
> + operator==(const _Iterator& __x, const _Sentinel& __y)
> + { return __y._M_equal(__x); }
> + };
> + };
> +
> + template<typename _Range, typename _Pattern>
> + split_view(_Range&&, _Pattern&&)
> + -> split_view<views::all_t<_Range>, views::all_t<_Pattern>>;
> +
> + template<forward_range _Range>
> + split_view(_Range&&, range_value_t<_Range>)
> + -> split_view<views::all_t<_Range>, single_view<range_value_t<_Range>>>;
> +
> + namespace views
> + {
> + namespace __detail
> + {
> + template<typename _Range, typename _Pattern>
> + concept __can_split_view
> + = requires { split_view(std::declval<_Range>(), std::declval<_Pattern>()); };
> + } // namespace __detail
> +
> + struct _Split : __adaptor::_RangeAdaptor<_Split>
> + {
> + template<viewable_range _Range, typename _Pattern>
> + requires __detail::__can_split_view<_Range, _Pattern>
> + constexpr auto
> + operator()(_Range&& __r, _Pattern&& __f) const
> + {
> + return split_view(std::forward<_Range>(__r), std::forward<_Pattern>(__f));
> + }
> +
> + using _RangeAdaptor<_Split>::operator();
> + static constexpr int _S_arity = 2;
> + template<typename _Pattern>
> + static constexpr bool _S_has_simple_extra_args
> + = _LazySplit::_S_has_simple_extra_args<_Pattern>;
> + };
> +
> + inline constexpr _Split split;
> + } // namespace views
> +
> namespace views
> {
> struct _Counted
> diff --git a/libstdc++-v3/testsuite/std/ranges/adaptors/100577.cc b/libstdc++-v3/testsuite/std/ranges/adaptors/100577.cc
> index 5ef7f3f59a7..81f2a62cfaa 100644
> --- a/libstdc++-v3/testsuite/std/ranges/adaptors/100577.cc
> +++ b/libstdc++-v3/testsuite/std/ranges/adaptors/100577.cc
> @@ -42,8 +42,6 @@ test01()
> static_assert(__adaptor_has_simple_extra_args<decltype(views::lazy_split), char>);
> static_assert(!__adaptor_has_simple_extra_args<decltype(views::lazy_split), std::string>);
>
> - // Verify all adaptor closures except for views::lazy_split(pattern) have a simple
> - // operator().
> using views::__adaptor::__closure_has_simple_call_op;
> __closure_has_simple_call_op auto a00 = views::all;
> __closure_has_simple_call_op auto a01 = views::transform(std::identity{});
> @@ -57,6 +55,7 @@ test01()
> __closure_has_simple_call_op auto a09 = views::reverse;
> __closure_has_simple_call_op auto a10 = views::keys;
> __closure_has_simple_call_op auto a11 = views::lazy_split(' ');
> + __closure_has_simple_call_op auto a11a = views::split(' ');
> // Verify composition of simple closures is simple.
> __closure_has_simple_call_op auto b
> = (a00 | a01) | (a02 | a03) | (a04 | a05 | a06) | (a07 | a08 | a09 | a10) | a11;
> @@ -67,6 +66,12 @@ test01()
> static_assert(!__closure_has_simple_call_op<decltype(a12)>);
> static_assert(!__closure_has_simple_call_op<decltype(a12 | a00)>);
> static_assert(!__closure_has_simple_call_op<decltype(a00 | a12)>);
> +
> + // Likewise views::split(non_view_range).
> + auto a12a = views::split(s);
> + static_assert(!__closure_has_simple_call_op<decltype(a12a)>);
> + static_assert(!__closure_has_simple_call_op<decltype(a12a | a00)>);
> + static_assert(!__closure_has_simple_call_op<decltype(a00 | a12a)>);
> }
>
> void
> @@ -98,6 +103,13 @@ test02()
> auto a1 = a0 | views::all;
> a1(x); // { dg-error "no match" }
>
> + views::lazy_split(badarg)(x); // { dg-error "deleted function" }
> + (views::lazy_split(badarg) | views::all)(x); // { dg-error "deleted function" }
> + auto a0a = views::split(badarg);
> + a0a(x); // { dg-error "no match" };
> + auto a1a = a0a | views::all;
> + a1a(x); // { dg-error "no match" }
> +
> views::take(badarg)(x); // { dg-error "deleted" }
> views::drop(badarg)(x); // { dg-error "deleted" }
> (views::take(badarg) | views::all)(x); // { dg-error "deleted" }
> diff --git a/libstdc++-v3/testsuite/std/ranges/adaptors/split.cc b/libstdc++-v3/testsuite/std/ranges/adaptors/split.cc
> new file mode 100644
> index 00000000000..9e6726cd07f
> --- /dev/null
> +++ b/libstdc++-v3/testsuite/std/ranges/adaptors/split.cc
> @@ -0,0 +1,196 @@
> +// Copyright (C) 2020-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++2a" }
> +// { dg-do run { target c++2a } }
> +
> +#include <algorithm>
> +#include <ranges>
> +#include <string>
> +#include <string_view>
> +#include <testsuite_hooks.h>
> +#include <testsuite_iterators.h>
> +
> +using __gnu_test::test_range;
> +using __gnu_test::forward_iterator_wrapper;
> +using __gnu_test::input_iterator_wrapper;
> +
> +namespace ranges = std::ranges;
> +namespace views = std::ranges::views;
> +
> +using namespace std::literals;
> +
> +void
> +test01()
> +{
> + auto from_chars = [] (auto v) {
> + return std::stoi(std::string(v.data(), v.data() + v.size()));
> + };
> + auto ints = "1.2.3.4"sv
> + | views::split('.')
> + | views::transform(from_chars);
> + VERIFY( ranges::equal(ints, (int[]){1,2,3,4}) );
> +}
> +
> +// The following testcases were adapted from lazy_split.cc.
> +namespace from_lazy_split_cc
> +{
> +void
> +test01()
> +{
> + auto x = "the quick brown fox"sv;
> + auto p = std::string{" "};
> + auto v = x | views::split(views::all(p)); // views::all is needed here after P2281.
> + auto i = v.begin();
> + VERIFY( ranges::equal(*i++, "the"sv) );
> + VERIFY( ranges::equal(*i++, "quick"sv) );
> + VERIFY( ranges::equal(*i++, "brown"sv) );
> + VERIFY( ranges::equal(*i++, "fox"sv) );
> + VERIFY( i == v.end() );
> +}
> +
> +void
> +test02()
> +{
> + auto x = "the quick brown fox"sv;
> + auto v = x | views::split(' ');
> + auto i = v.begin();
> + VERIFY( ranges::equal(*i++, "the"sv) );
> + VERIFY( ranges::equal(*i++, "quick"sv) );
> + VERIFY( ranges::equal(*i++, "brown"sv) );
> + VERIFY( ranges::equal(*i++, "fox"sv) );
> + VERIFY( i == v.end() );
> +}
> +
> +void
> +test03()
> +{
> + char x[] = "the quick brown fox";
> + test_range<char, forward_iterator_wrapper> rx(x, x+sizeof(x)-1);
> + auto v = rx | views::split(' ');
> + auto i = v.begin();
> + VERIFY( ranges::equal(*i++, "the"sv) );
> + VERIFY( ranges::equal(*i++, "quick"sv) );
> + VERIFY( ranges::equal(*i++, "brown"sv) );
> + VERIFY( ranges::equal(*i++, "fox"sv) );
> + VERIFY( i == v.end() );
> +}
> +
> +void
> +test04()
> +{
> + auto x = "the quick brown fox"sv;
> + std::initializer_list<char> p = {' ', ' '};
> + static_assert(!ranges::view<decltype(p)>);
> + static_assert(std::same_as<decltype(p | views::all),
> + ranges::ref_view<decltype(p)>>);
> + auto v = x | views::split(views::all(p)); // views::all is needed here after P2281.
> + auto i = v.begin();
> + VERIFY( ranges::equal(*i++, "the"sv) );
> + VERIFY( ranges::equal(*i++, "quick"sv) );
> + VERIFY( ranges::equal(*i++, "brown"sv) );
> + VERIFY( ranges::equal(*i++, "fox"sv) );
> + VERIFY( i == v.end() );
> +}
> +
> +void
> +test05()
> +{
> + auto as_string = [](ranges::view auto rng) {
> + auto in = rng | views::common;
> + return std::string(in.begin(), in.end());
> + };
> + std::string str
> + = "Now is the time for all good men to come to the aid of their county.";
> + auto rng
> + = str | views::split(' ') | views::transform(as_string) | views::common;
> + std::vector<std::string> words(rng.begin(), rng.end());
> + auto not_space_p = [](char c) { return c != ' '; };
> + VERIFY( ranges::equal(words | views::join,
> + str | views::filter(not_space_p)) );
> +}
> +
> +template<auto split = views::split>
> +void
> +test06()
> +{
> + // Verify SFINAE behavior.
> + std::string s, p;
> + static_assert(!requires { split(); });
> + static_assert(!requires { split(s, p, 0); });
> + static_assert(!requires { split(p)(); });
> + static_assert(!requires { s | split; });
> +
> + static_assert(!requires { s | split(p); });
> + static_assert(!requires { split(p)(s); });
> + static_assert(!requires { s | (split(p) | views::all); });
> + static_assert(!requires { (split(p) | views::all)(s); });
> +
> + static_assert(requires { s | split(views::all(p)); });
> + static_assert(requires { split(views::all(p))(s); });
> + static_assert(requires { s | (split(views::all(p)) | views::all); });
> + static_assert(requires { (split(views::all(p)) | views::all)(s); });
> +
> + auto adapt = split(p);
> + static_assert(requires { s | adapt; });
> + static_assert(requires { adapt(s); });
> +
> + auto adapt2 = split(p) | views::all;
> + static_assert(requires { s | adapt2; });
> + static_assert(requires { adapt2(s); });
> +}
> +
> +void
> +test10()
> +{
> + // LWG 3505
> + auto to_string = [] (auto r) {
> + return std::string(r.begin(), ranges::next(r.begin(), r.end()));
> + };
> + auto v = "xxyx"sv | views::split("xy"sv) | views::transform(to_string);
> + VERIFY( ranges::equal(v, (std::string_view[]){"x", "x"}) );
> +}
> +
> +void
> +test11()
> +{
> + // LWG 3478
> + static_assert(ranges::distance(views::split("text"sv, "text"sv)) == 2);
> + static_assert(ranges::distance(views::split(" text "sv, ' ')) == 3);
> + static_assert(ranges::distance(views::split(" t e x t "sv, ' ')) == 6);
> + static_assert(ranges::distance(views::split(" text "sv, " "sv)) == 3);
> + static_assert(ranges::distance(views::split(" text "sv, " "sv)) == 4);
> + static_assert(ranges::distance(views::split(" text "sv, " "sv)) == 4);
> + static_assert(ranges::distance(views::split("t"sv, 't')) == 2);
> + static_assert(ranges::distance(views::split("text"sv, ""sv)) == 4);
> +}
> +} // namespace from_lazy_split_cc
> +
> +int
> +main()
> +{
> + test01();
> +
> + from_lazy_split_cc::test01();
> + from_lazy_split_cc::test02();
> + from_lazy_split_cc::test03();
> + from_lazy_split_cc::test04();
> + from_lazy_split_cc::test05();
> + from_lazy_split_cc::test06();
> + from_lazy_split_cc::test10();
> + from_lazy_split_cc::test11();
> +}
> diff --git a/libstdc++-v3/testsuite/std/ranges/p2325.cc b/libstdc++-v3/testsuite/std/ranges/p2325.cc
> index 4d075409026..d2ebe9af863 100644
> --- a/libstdc++-v3/testsuite/std/ranges/p2325.cc
> +++ b/libstdc++-v3/testsuite/std/ranges/p2325.cc
> @@ -124,6 +124,20 @@ test08()
> static_assert(default_initializable<type4>);
> }
>
> +void
> +test08a()
> +{
> + // Verify split_view is conditionally default constructible.
> + using type1 = ranges::split_view<ranges::ref_view<int[2]>, ranges::single_view<int>>;
> + static_assert(!default_initializable<type1>);
> + using type2 = ranges::split_view<ranges::single_view<int>, ranges::ref_view<int[2]>>;
> + static_assert(!default_initializable<type2>);
> + using type3 = ranges::split_view<ranges::ref_view<int[2]>, ranges::ref_view<int[2]>>;
> + static_assert(!default_initializable<type3>);
> + using type4 = ranges::split_view<ranges::single_view<int>, ranges::single_view<int>>;
> + static_assert(default_initializable<type4>);
> +}
> +
> void
> test09()
> {
> diff --git a/libstdc++-v3/testsuite/std/ranges/p2367.cc b/libstdc++-v3/testsuite/std/ranges/p2367.cc
> index 5228b021602..70a0304593f 100644
> --- a/libstdc++-v3/testsuite/std/ranges/p2367.cc
> +++ b/libstdc++-v3/testsuite/std/ranges/p2367.cc
> @@ -45,4 +45,5 @@ test01()
>
> // Verify changes to views::lazy_split.
> auto v6 = views::lazy_split(x, 5u);
> + auto v7 = views::split(x, 5u);
> }
> --
> 2.32.0.93.g670b81a890
>
^ permalink raw reply [flat|nested] 11+ messages in thread
end of thread, other threads:[~2021-06-18 21:58 UTC | newest]
Thread overview: 11+ messages (download: mbox.gz / follow: Atom feed)
-- links below jump to the message on this page --
2021-06-17 15:22 [PATCH 1/5] libstdc++: Implement P2325 changes to default-constructibilty of views Patrick Palka
2021-06-17 15:22 ` [PATCH 2/5] libstdc++: Move ranges algos used by <ranges> into ranges_util.h Patrick Palka
2021-06-17 18:14 ` Jonathan Wakely
2021-06-17 15:22 ` [PATCH 3/5] libstdc++: Rename views::split to views::lazy_split as per P2210 Patrick Palka
2021-06-18 21:56 ` Jonathan Wakely
2021-06-17 15:22 ` [PATCH 4/5] libstdc++: Implement resolution of LWG 3478 " Patrick Palka
2021-06-18 21:57 ` Jonathan Wakely
2021-06-17 15:22 ` [PATCH 5/5] libstdc++: Implement new views::split " Patrick Palka
2021-06-18 21:58 ` Jonathan Wakely
2021-06-17 18:19 ` [PATCH 1/5] libstdc++: Implement P2325 changes to default-constructibilty of views Jonathan Wakely
2021-06-17 18:28 ` Jonathan Wakely
This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox;
as well as URLs for read-only IMAP folder(s) and NNTP newsgroup(s).