https://gcc.gnu.org/g:7a4fced2b122bde47fed2d99fb8e3cf197f9c46f
commit r15-4853-g7a4fced2b122bde47fed2d99fb8e3cf197f9c46f Author: Jonathan Wakely <jwak...@redhat.com> Date: Tue Oct 8 21:15:18 2024 +0100 libstdc++: Add P1206R7 from_range members to std::list and std::forward_list [PR111055] This is another piece of P1206R7, adding new members to std::list and std::forward_list. libstdc++-v3/ChangeLog: PR libstdc++/111055 * include/bits/forward_list.h (forward_list(from_range, R&&, const Alloc&), assign_range) (prepend_range, insert_range_after): Define. * include/bits/stl_list.h (list(from_range, R&&, const Alloc&)) (assign_range, prepend_range, append_range, insert_range): Define. * include/debug/forward_list (forward_list(from_range, R&&, const Alloc&), assign_range) (prepend_range, insert_range_after): Define. * include/debug/list (list(from_range, R&&, const Alloc&)) (assign_range, prepend_range, append_range, insert_range): Define. * testsuite/23_containers/forward_list/cons/from_range.cc: New test. * testsuite/23_containers/forward_list/modifiers/assign_range.cc: New test. * testsuite/23_containers/forward_list/modifiers/insert_range_after.cc: New test. * testsuite/23_containers/forward_list/modifiers/prepend_range.cc: New test. * testsuite/23_containers/list/cons/from_range.cc: New test. * testsuite/23_containers/list/modifiers/append_range.cc: New test. * testsuite/23_containers/list/modifiers/assign/assign_range.cc: New test. * testsuite/23_containers/list/modifiers/insert/insert_range.cc: New test. * testsuite/23_containers/list/modifiers/prepend_range.cc: New test. Reviewed-by: Patrick Palka <ppa...@redhat.com> Diff: --- libstdc++-v3/include/bits/forward_list.h | 121 +++++++++++++++++++ libstdc++-v3/include/bits/stl_list.h | 128 +++++++++++++++++++++ libstdc++-v3/include/debug/forward_list | 63 ++++++++++ libstdc++-v3/include/debug/list | 56 +++++++++ .../23_containers/forward_list/cons/from_range.cc | 100 ++++++++++++++++ .../forward_list/modifiers/assign_range.cc | 97 ++++++++++++++++ .../forward_list/modifiers/insert_range_after.cc | 93 +++++++++++++++ .../forward_list/modifiers/prepend_range.cc | 86 ++++++++++++++ .../23_containers/list/cons/from_range.cc | 100 ++++++++++++++++ .../23_containers/list/modifiers/append_range.cc | 86 ++++++++++++++ .../list/modifiers/assign/assign_range.cc | 99 ++++++++++++++++ .../list/modifiers/insert/insert_range.cc | 98 ++++++++++++++++ .../23_containers/list/modifiers/prepend_range.cc | 86 ++++++++++++++ 13 files changed, 1213 insertions(+) diff --git a/libstdc++-v3/include/bits/forward_list.h b/libstdc++-v3/include/bits/forward_list.h index eee773c02121..c9238cef96fa 100644 --- a/libstdc++-v3/include/bits/forward_list.h +++ b/libstdc++-v3/include/bits/forward_list.h @@ -42,6 +42,10 @@ #include <bits/allocator.h> #include <ext/alloc_traits.h> #include <ext/aligned_buffer.h> +#if __glibcxx_ranges_to_container // C++ >= 23 +# include <bits/ranges_base.h> // ranges::begin, ranges::distance etc. +# include <bits/ranges_util.h> // ranges::subrange +#endif namespace std _GLIBCXX_VISIBILITY(default) { @@ -564,6 +568,30 @@ _GLIBCXX_BEGIN_NAMESPACE_CONTAINER : _Base(_Node_alloc_type(__al)) { _M_range_initialize(__first, __last); } +#if __glibcxx_ranges_to_container // C++ >= 23 + /** + * @brief Construct a forward_list from a range. + * @param __rg An input range with elements that are convertible to + * the forward_list's value_type. + * @param __a An allocator. + * + * @since C++23 + */ + template<__detail::__container_compatible_range<_Tp> _Rg> + forward_list(from_range_t, _Rg&& __rg, const _Alloc& __a = _Alloc()) + : _Base(_Node_alloc_type(__a)) + { + _Node_base* __to = &this->_M_impl._M_head; + auto __first = ranges::begin(__rg); + const auto __last = ranges::end(__rg); + for (; __first != __last; ++__first) + { + __to->_M_next = this->_M_create_node(*__first); + __to = __to->_M_next; + } + } +#endif // ranges_to_container + /** * @brief The %forward_list copy constructor. * @param __list A %forward_list of identical element and allocator @@ -675,6 +703,39 @@ _GLIBCXX_BEGIN_NAMESPACE_CONTAINER _M_assign(__first, __last, __assignable()); } +#if __glibcxx_ranges_to_container // C++ >= 23 + /** + * @brief Assign a range to a forward_list. + * @since C++23 + */ + template<__detail::__container_compatible_range<_Tp> _Rg> + void + assign_range(_Rg&& __rg) + { + static_assert(assignable_from<_Tp&, ranges::range_reference_t<_Rg>>); + + auto __first = ranges::begin(__rg); + const auto __last = ranges::end(__rg); + iterator __prev = before_begin(); + iterator __curr = begin(); + const iterator __end = end(); + + while (__curr != __end && __first != __last) + { + *__curr = *__first; + __prev = __curr; + ++__first; + ++__curr; + } + + if (__curr != __end) + erase_after(__prev, __end); + else + insert_range_after(__prev, + ranges::subrange(std::move(__first), __last)); + } +#endif // ranges_to_container + /** * @brief Assigns a given value to a %forward_list. * @param __n Number of elements to be assigned. @@ -888,6 +949,33 @@ _GLIBCXX_BEGIN_NAMESPACE_CONTAINER push_front(_Tp&& __val) { this->_M_insert_after(cbefore_begin(), std::move(__val)); } +#if __glibcxx_ranges_to_container // C++ >= 23 + /** + * @brief Insert a range at the beginning of a forward_list. + * @param __rg An input range with elements that are convertible to + * the forward_list's value_type. + * + * The inserted elements will be in the same order as in the range, + * so they are not reversed as would happen with a simple loop calling + * emplace_front for each element of the range. + * + * No iterators to existing elements are invalidated by this function. + * If the insertion fails due to an exception, no elements will be added + * and so the list will be unchanged. + * + * @since C++23 + */ + template<__detail::__container_compatible_range<_Tp> _Rg> + void + prepend_range(_Rg&& __rg) + { + forward_list __tmp(from_range, std::forward<_Rg>(__rg), + get_allocator()); + if (!__tmp.empty()) + splice_after(before_begin(), __tmp); + } +#endif // ranges_to_container + /** * @brief Removes first element. * @@ -1004,6 +1092,32 @@ _GLIBCXX_BEGIN_NAMESPACE_CONTAINER insert_after(const_iterator __pos, std::initializer_list<_Tp> __il) { return insert_after(__pos, __il.begin(), __il.end()); } +#if __glibcxx_ranges_to_container // C++ >= 23 + /** + * @brief Insert a rangeinto a forward_list. + * @param __position An iterator. + * @param __rg An input range of elements that can be converted to + * the forward_list's value type. + * @return An iterator pointing to the last element inserted, + * or `__position` if the range is empty. + * + * Inserts the elements of `__rg` after `__position`. + * No iterators to existing elements are invalidated by this function. + * If the insertion fails due to an exception, no elements will be added + * and so the list will be unchanged. + * + * @since C++23 + */ + template<__detail::__container_compatible_range<_Tp> _Rg> + iterator + insert_range_after(const_iterator __position, _Rg&& __rg) + { + forward_list __tmp(from_range, std::forward<_Rg>(__rg), + get_allocator()); + return _M_splice_after(__position, __tmp.before_begin(), __tmp.end()); + } +#endif // ranges_to_container + /** * @brief Removes the element pointed to by the iterator following * @c pos. @@ -1438,6 +1552,13 @@ _GLIBCXX_BEGIN_NAMESPACE_CONTAINER typename = _RequireAllocator<_Allocator>> forward_list(_InputIterator, _InputIterator, _Allocator = _Allocator()) -> forward_list<_ValT, _Allocator>; + +#if __glibcxx_ranges_to_container // C++ >= 23 + template<ranges::input_range _Rg, + typename _Allocator = allocator<ranges::range_value_t<_Rg>>> + forward_list(from_range_t, _Rg&&, _Allocator = _Allocator()) + -> forward_list<ranges::range_value_t<_Rg>, _Allocator>; +#endif #endif /** diff --git a/libstdc++-v3/include/bits/stl_list.h b/libstdc++-v3/include/bits/stl_list.h index 8b2521960a8f..3c313cec1d89 100644 --- a/libstdc++-v3/include/bits/stl_list.h +++ b/libstdc++-v3/include/bits/stl_list.h @@ -64,6 +64,10 @@ #include <bits/allocated_ptr.h> #include <ext/aligned_buffer.h> #endif +#if __glibcxx_ranges_to_container // C++ >= 23 +# include <bits/ranges_base.h> // ranges::begin, ranges::distance etc. +# include <bits/ranges_util.h> // ranges::subrange +#endif namespace std _GLIBCXX_VISIBILITY(default) { @@ -892,6 +896,22 @@ _GLIBCXX_BEGIN_NAMESPACE_CXX11 } #endif +#if __glibcxx_ranges_to_container // C++ >= 23 + /** + * @brief Construct a list from a range. + * @since C++23 + */ + template<__detail::__container_compatible_range<_Tp> _Rg> + list(from_range_t, _Rg&& __rg, const _Alloc& __a = _Alloc()) + : _Base(_Node_alloc_type(__a)) + { + auto __first = ranges::begin(__rg); + const auto __last = ranges::end(__rg); + for (; __first != __last; ++__first) + emplace_back(*__first); + } +#endif + #if __cplusplus >= 201103L /** * No explicit dtor needed as the _Base dtor takes care of @@ -951,6 +971,32 @@ _GLIBCXX_BEGIN_NAMESPACE_CXX11 } #endif +#if __glibcxx_ranges_to_container // C++ >= 23 + /** + * @brief Assign a range to a list. + * @since C++23 + */ + template<__detail::__container_compatible_range<_Tp> _Rg> + void + assign_range(_Rg&& __rg) + { + static_assert(assignable_from<_Tp&, ranges::range_reference_t<_Rg>>); + + iterator __first1 = begin(); + const iterator __last1 = end(); + auto __first2 = ranges::begin(__rg); + const auto __last2 = ranges::end(__rg); + for (; __first1 != __last1 && __first2 != __last2; + ++__first1, (void)++__first2) + *__first1 = *__first2; + if (__first2 == __last2) + erase(__first1, __last1); + else + insert_range(__last1, + ranges::subrange(std::move(__first2), __last2)); + } +#endif + /** * @brief Assigns a given value to a %list. * @param __n Number of elements to be assigned. @@ -1275,6 +1321,50 @@ _GLIBCXX_BEGIN_NAMESPACE_CXX11 } #endif +#if __glibcxx_ranges_to_container // C++ >= 23 + /** + * @brief Insert a range at the beginning of a list. + * @param __rg An input range of elements that can be converted to + * the list's value type. + * + * Inserts the elements of `__rg` at the beginning of the list. + * No iterators to existing elements are invalidated by this function. + * If the insertion fails due to an exception, no elements will be added + * and so the list will be unchanged. + * + * @since C++23 + */ + template<__detail::__container_compatible_range<_Tp> _Rg> + void + prepend_range(_Rg&& __rg) + { + list __tmp(from_range, std::forward<_Rg>(__rg), get_allocator()); + if (!__tmp.empty()) + splice(begin(), __tmp); + } + + /** + * @brief Insert a range at the end of a list. + * @param __rg An input range of elements that can be converted to + * the list's value type. + * + * Inserts the elements of `__rg` at the end of the list. + * No iterators to existing elements are invalidated by this function. + * If the insertion fails due to an exception, no elements will be added + * and so the list will be unchanged. + * + * @since C++23 + */ + template<__detail::__container_compatible_range<_Tp> _Rg> + void + append_range(_Rg&& __rg) + { + list __tmp(from_range, std::forward<_Rg>(__rg), get_allocator()); + if (!__tmp.empty()) + splice(end(), __tmp); + } +#endif + /** * @brief Removes first element. * @@ -1505,6 +1595,37 @@ _GLIBCXX_BEGIN_NAMESPACE_CXX11 } #endif +#if __glibcxx_ranges_to_container // C++ >= 23 + /** + * @brief Insert a range into a list. + * @param __position An iterator. + * @param __rg An input range of elements that can be converted to + * the list's value type. + * @return An iterator pointing to the first element inserted, + * or `__position` if the range is empty. + * + * Inserts the elements of `__rg` before `__position`. + * No iterators to existing elements are invalidated by this function. + * If the insertion fails due to an exception, no elements will be added + * and so the list will be unchanged. + * + * @since C++23 + */ + template<__detail::__container_compatible_range<_Tp> _Rg> + iterator + insert_range(const_iterator __position, _Rg&& __rg) + { + list __tmp(from_range, std::forward<_Rg>(__rg), get_allocator()); + if (!__tmp.empty()) + { + auto __it = __tmp.begin(); + splice(__position, __tmp); + return __it; + } + return __position._M_const_cast(); + } +#endif + /** * @brief Remove element at given position. * @param __position Iterator pointing to element to be erased. @@ -2102,6 +2223,13 @@ _GLIBCXX_BEGIN_NAMESPACE_CXX11 typename = _RequireAllocator<_Allocator>> list(_InputIterator, _InputIterator, _Allocator = _Allocator()) -> list<_ValT, _Allocator>; + +#if __glibcxx_ranges_to_container // C++ >= 23 + template<ranges::input_range _Rg, + typename _Allocator = allocator<ranges::range_value_t<_Rg>>> + list(from_range_t, _Rg&&, _Allocator = _Allocator()) + -> list<ranges::range_value_t<_Rg>, _Allocator>; +#endif #endif _GLIBCXX_END_NAMESPACE_CXX11 diff --git a/libstdc++-v3/include/debug/forward_list b/libstdc++-v3/include/debug/forward_list index 3f94a9098e86..73389c466771 100644 --- a/libstdc++-v3/include/debug/forward_list +++ b/libstdc++-v3/include/debug/forward_list @@ -267,6 +267,13 @@ namespace __debug __gnu_debug::__base(__last), __al) { } +#if __glibcxx_ranges_to_container // C++ >= 23 + template<__detail::__container_compatible_range<_Tp> _Rg> + forward_list(from_range_t, _Rg&& __rg, const _Alloc& __a = _Alloc()) + : _Base(std::from_range, std::forward<_Rg>(__rg), __a) + { } +#endif + forward_list(const forward_list&) = default; forward_list(forward_list&&) = default; @@ -311,6 +318,39 @@ namespace __debug this->_M_invalidate_all(); } +#if __glibcxx_ranges_to_container // C++ >= 23 + template<__detail::__container_compatible_range<_Tp> _Rg> + void + assign_range(_Rg&& __rg) + { + // Have to reimplement this function, so that we use the debug + // version of erase_after, which invalidates the correct iterators. + + static_assert(assignable_from<_Tp&, ranges::range_reference_t<_Rg>>); + + auto __first = ranges::begin(__rg); + const auto __last = ranges::end(__rg); + auto __prev = _Base::before_begin(); + auto __curr = _Base::begin(); + const auto __end = _Base::end(); + + while (__curr != __end && __first != __last) + { + *__curr = *__first; + __prev = __curr; + ++__first; + ++__curr; + } + + if (__curr != __end) + erase_after(const_iterator(__prev, this), end()); + else + _Base::insert_range_after(__prev, + ranges::subrange(std::move(__first), + __last)); + } +#endif + void assign(size_type __n, const _Tp& __val) { @@ -400,6 +440,10 @@ namespace __debug using _Base::emplace_front; using _Base::push_front; +#if __glibcxx_ranges_to_container // C++ >= 23 + using _Base::prepend_range; +#endif + void pop_front() { @@ -468,6 +512,18 @@ namespace __debug return { _Base::insert_after(__pos.base(), __il), this }; } +#if __glibcxx_ranges_to_container // C++ >= 23 + template<__detail::__container_compatible_range<_Tp> _Rg> + iterator + insert_range_after(const_iterator __position, _Rg&& __rg) + { + auto __ret + = _Base::insert_range_after(__position.base(), + std::forward<_Rg>(__rg)); + return { __ret, this }; + } +#endif + iterator erase_after(const_iterator __pos) { @@ -860,6 +916,13 @@ namespace __debug typename = _RequireAllocator<_Allocator>> forward_list(size_t, _Tp, _Allocator = _Allocator()) -> forward_list<_Tp, _Allocator>; + +#if __glibcxx_ranges_to_container // C++ >= 23 + template<ranges::input_range _Rg, + typename _Allocator = allocator<ranges::range_value_t<_Rg>>> + forward_list(from_range_t, _Rg&&, _Allocator = _Allocator()) + -> forward_list<ranges::range_value_t<_Rg>, _Allocator>; +#endif #endif template<typename _Tp, typename _Alloc> diff --git a/libstdc++-v3/include/debug/list b/libstdc++-v3/include/debug/list index 60752b4dc8b1..4028a3d39856 100644 --- a/libstdc++-v3/include/debug/list +++ b/libstdc++-v3/include/debug/list @@ -160,6 +160,13 @@ namespace __debug __gnu_debug::__base(__last), __a) { } +#if __glibcxx_ranges_to_container // C++ >= 23 + template<__detail::__container_compatible_range<_Tp> _Rg> + list(from_range_t, _Rg&& __rg, const _Allocator& __a = _Allocator()) + : _Base(std::from_range, std::forward<_Rg>(__rg), __a) + { } +#endif + list(_Base_ref __x) : _Base(__x._M_ref) { } @@ -207,6 +214,32 @@ namespace __debug this->_M_invalidate_all(); } +#if __glibcxx_ranges_to_container // C++ >= 23 + template<__detail::__container_compatible_range<_Tp> _Rg> + void + assign_range(_Rg&& __rg) + { + // Have to reimplement this function, so that we use the debug + // version of erase, which invalidates the correct iterators. + + static_assert(assignable_from<_Tp&, ranges::range_reference_t<_Rg>>); + + auto __first1 = _Base::begin(); + const auto __last1 = _Base::end(); + auto __first2 = ranges::begin(__rg); + const auto __last2 = ranges::end(__rg); + for (; __first1 != __last1 && __first2 != __last2; + ++__first1, (void)++__first2) + *__first1 = *__first2; + if (__first2 == __last2) + erase(const_iterator(__first1, this), + const_iterator(__last1, this)); + else + _Base::insert_range(__last1, + ranges::subrange(std::move(__first2), __last2)); + } +#endif + void assign(size_type __n, const _Tp& __t) { @@ -401,6 +434,11 @@ namespace __debug using _Base::emplace_front; #endif +#if __glibcxx_ranges_to_container // C++ >= 23 + using _Base::prepend_range; + using _Base::append_range; +#endif + void pop_front() _GLIBCXX_NOEXCEPT { @@ -511,6 +549,17 @@ namespace __debug } #endif +#if __glibcxx_ranges_to_container // C++ >= 23 + template<__detail::__container_compatible_range<_Tp> _Rg> + iterator + insert_range(const_iterator __position, _Rg&& __rg) + { + auto __ret = _Base::insert_range(__position.base(), + std::forward<_Rg>(__rg)); + return { __ret, this }; + } +#endif + private: _Base_iterator #if __cplusplus >= 201103L @@ -920,6 +969,13 @@ namespace __debug typename = _RequireAllocator<_Allocator>> list(size_t, _Tp, _Allocator = _Allocator()) -> list<_Tp, _Allocator>; + +#if __glibcxx_ranges_to_container // C++ >= 23 + template<ranges::input_range _Rg, + typename _Allocator = allocator<ranges::range_value_t<_Rg>>> + list(from_range_t, _Rg&&, _Allocator = _Allocator()) + -> list<ranges::range_value_t<_Rg>, _Allocator>; +#endif #endif template<typename _Tp, typename _Alloc> diff --git a/libstdc++-v3/testsuite/23_containers/forward_list/cons/from_range.cc b/libstdc++-v3/testsuite/23_containers/forward_list/cons/from_range.cc new file mode 100644 index 000000000000..65b378e69773 --- /dev/null +++ b/libstdc++-v3/testsuite/23_containers/forward_list/cons/from_range.cc @@ -0,0 +1,100 @@ +// { dg-do run { target c++23 } } + +#include <forward_list> +#include <span> +#include <testsuite_hooks.h> +#include <testsuite_iterators.h> +#include <testsuite_allocator.h> + +void +test_deduction_guide(long* p) +{ + __gnu_test::test_input_range<long> r(p, p); + std::forward_list v(std::from_range, r); + static_assert(std::is_same_v<decltype(v), std::forward_list<long>>); + + using Alloc = __gnu_test::SimpleAllocator<long>; + Alloc alloc; + std::forward_list v2(std::from_range, r, alloc); + static_assert(std::is_same_v<decltype(v2), std::forward_list<long, Alloc>>); +} + +template<typename Range, typename Alloc> +constexpr void +do_test(Alloc alloc) +{ + // The forward_list's value_type. + using V = typename std::allocator_traits<Alloc>::value_type; + + // The range's value_type. + using T = std::ranges::range_value_t<Range>; + T a[]{1,2,3,4,5,6,7,8,9}; + + auto eq = [](const std::forward_list<V, Alloc>& l, std::span<T> r) { + if (std::distance(l.begin(), l.end()) != r.size()) + return false; + unsigned i = 0; + for (auto& e : l) + if (e != r[i++]) + return false; + return true; + }; + + std::forward_list<V, Alloc> v0(std::from_range, Range(a, a+0)); + VERIFY( v0.empty() ); + VERIFY( v0.get_allocator() == Alloc() ); + + std::forward_list<V, Alloc> v4(std::from_range, Range(a, a+4)); + VERIFY( eq(v4, {a, 4}) ); + VERIFY( v4.get_allocator() == Alloc() ); + + std::forward_list<V, Alloc> v9(std::from_range, Range(a, a+9), alloc); + VERIFY( eq(v9, {a, 9}) ); + VERIFY( v9.get_allocator() == alloc ); +} + +template<typename Range> +void +do_test_a() +{ + do_test<Range>(std::allocator<int>()); + do_test<Range>(__gnu_test::uneq_allocator<int>(42)); +} + +bool +test_ranges() +{ + using namespace __gnu_test; + + do_test_a<test_forward_range<int>>(); + do_test_a<test_forward_sized_range<int>>(); + do_test_a<test_sized_range_sized_sent<int, forward_iterator_wrapper>>(); + + do_test_a<test_input_range<int>>(); + do_test_a<test_input_sized_range<int>>(); + do_test_a<test_sized_range_sized_sent<int, input_iterator_wrapper>>(); + + do_test_a<test_range<int, input_iterator_wrapper_nocopy>>(); + do_test_a<test_sized_range<int, input_iterator_wrapper_nocopy>>(); + do_test_a<test_sized_range_sized_sent<int, input_iterator_wrapper_nocopy>>(); + + do_test_a<test_forward_range<short>>(); + do_test_a<test_input_range<short>>(); + + // Not lvalue-convertible to bool + struct C { + C(int v) : val(v) { } + operator int() && { return val; } + bool operator==(int b) const { return b == val; } + int val; + }; + using rvalue_input_range = test_range<C, input_iterator_wrapper_rval>; + do_test<rvalue_input_range>(std::allocator<int>()); + + return true; +} + +int main() +{ + test_ranges(); +} diff --git a/libstdc++-v3/testsuite/23_containers/forward_list/modifiers/assign_range.cc b/libstdc++-v3/testsuite/23_containers/forward_list/modifiers/assign_range.cc new file mode 100644 index 000000000000..d8352539724b --- /dev/null +++ b/libstdc++-v3/testsuite/23_containers/forward_list/modifiers/assign_range.cc @@ -0,0 +1,97 @@ +// { dg-do run { target c++23 } } + +#include <forward_list> +#include <span> +#include <testsuite_hooks.h> +#include <testsuite_iterators.h> +#include <testsuite_allocator.h> + +template<typename Range, typename Alloc> +constexpr void +do_test() +{ + // The forward_list's value_type. + using V = typename std::allocator_traits<Alloc>::value_type; + + // The range's value_type. + using T = std::ranges::range_value_t<Range>; + T a[]{1,2,3,4,5,6,7,8,9}; + + auto eq = [](const std::forward_list<V, Alloc>& l, std::span<T> r) { + if (std::distance(l.begin(), l.end()) != r.size()) + return false; + unsigned i = 0; + for (auto& e : l) + if (e != r[i++]) + return false; + return true; + }; + + // assign to empty forward_list + std::forward_list<V, Alloc> v; + v.assign_range(Range(a, a)); + VERIFY( v.empty() ); + v.assign_range(Range(a, a+4)); + VERIFY( eq(v, {a, 4}) ); + v.clear(); + v.assign_range(Range(a)); + VERIFY( eq(v, a) ); + + // assign to non-empty forward_list + v.assign_range(Range(a, a+4)); + VERIFY( eq(v, {a, 4}) ); + v.assign_range(Range(a)); // larger than size() + VERIFY( eq(v, a) ); + v.assign_range(Range(a, a+4)); // smaller than size() + VERIFY( eq(v, {a, 4}) ); + v.assign_range(Range(a+2, a+6)); // equal to size() + VERIFY( eq(v, {a+2, 4}) ); + v.assign_range(Range(a, a)); + VERIFY( v.empty() ); +} + +template<typename Range> +void +do_test_a() +{ + do_test<Range, std::allocator<int>>(); + do_test<Range, __gnu_test::SimpleAllocator<int>>(); +} + +bool +test_ranges() +{ + using namespace __gnu_test; + + do_test_a<test_forward_range<int>>(); + do_test_a<test_forward_sized_range<int>>(); + do_test_a<test_sized_range_sized_sent<int, forward_iterator_wrapper>>(); + + do_test_a<test_input_range<int>>(); + do_test_a<test_input_sized_range<int>>(); + do_test_a<test_sized_range_sized_sent<int, input_iterator_wrapper>>(); + + do_test_a<test_range<int, input_iterator_wrapper_nocopy>>(); + do_test_a<test_sized_range<int, input_iterator_wrapper_nocopy>>(); + do_test_a<test_sized_range_sized_sent<int, input_iterator_wrapper_nocopy>>(); + + do_test_a<test_forward_range<short>>(); + do_test_a<test_input_range<short>>(); + + // Not lvalue-convertible to int + struct C { + C(int v) : val(v) { } + operator int() && { return val; } + bool operator==(int b) const { return b == val; } + int val; + }; + using rvalue_input_range = test_range<C, input_iterator_wrapper_rval>; + do_test<rvalue_input_range, std::allocator<int>>(); + + return true; +} + +int main() +{ + test_ranges(); +} diff --git a/libstdc++-v3/testsuite/23_containers/forward_list/modifiers/insert_range_after.cc b/libstdc++-v3/testsuite/23_containers/forward_list/modifiers/insert_range_after.cc new file mode 100644 index 000000000000..32580a6ed1e2 --- /dev/null +++ b/libstdc++-v3/testsuite/23_containers/forward_list/modifiers/insert_range_after.cc @@ -0,0 +1,93 @@ +// { dg-do run { target c++23 } } + +#include <forward_list> +#include <span> +#include <testsuite_hooks.h> +#include <testsuite_iterators.h> +#include <testsuite_allocator.h> + +template<typename Range, typename Alloc> +constexpr void +do_test() +{ + // The forward_list's value_type. + using V = typename std::allocator_traits<Alloc>::value_type; + + // The range's value_type. + using T = std::ranges::range_value_t<Range>; + T a[]{1,2,3,4,5,6,7,8,9}; + + auto eq = [](const std::forward_list<V, Alloc>& l, std::span<T> r) { + if (std::distance(l.begin(), l.end()) != r.size()) + return false; + unsigned i = 0; + for (auto& e : l) + if (e != r[i++]) + return false; + return true; + }; + + std::forward_list<V, Alloc> v; + v.insert_range_after(v.before_begin(), Range(a, a)); + VERIFY( v.empty() ); + v.insert_range_after(v.before_begin(), Range(a, a+4)); + VERIFY( eq(v, {a, a+4}) ); + v.clear(); + v.insert_range_after(v.before_begin(), Range(a+4, a+9)); + VERIFY( eq(v, {a+4, a+9}) ); + v.insert_range_after(v.before_begin(), Range(a, a+4)); + VERIFY( eq(v, a) ); + v.clear(); + v.insert_range_after(v.before_begin(), Range(a, a+3)); + v.insert_range_after(std::next(v.before_begin(), 3), Range(a+6, a+9)); + v.insert_range_after(std::next(v.before_begin(), 3), Range(a+3, a+6)); + VERIFY( eq(v, a) ); + v.insert_range_after(std::next(v.before_begin(), 2), Range(a, a)); + VERIFY( eq(v, a) ); +} + +template<typename Range> +void +do_test_a() +{ + do_test<Range, std::allocator<int>>(); + do_test<Range, __gnu_test::SimpleAllocator<int>>(); +} + +bool +test_ranges() +{ + using namespace __gnu_test; + + do_test_a<test_forward_range<int>>(); + do_test_a<test_forward_sized_range<int>>(); + do_test_a<test_sized_range_sized_sent<int, forward_iterator_wrapper>>(); + + do_test_a<test_input_range<int>>(); + do_test_a<test_input_sized_range<int>>(); + do_test_a<test_sized_range_sized_sent<int, input_iterator_wrapper>>(); + + do_test_a<test_range<int, input_iterator_wrapper_nocopy>>(); + do_test_a<test_sized_range<int, input_iterator_wrapper_nocopy>>(); + do_test_a<test_sized_range_sized_sent<int, input_iterator_wrapper_nocopy>>(); + + do_test_a<test_forward_range<short>>(); + do_test_a<test_input_range<short>>(); + + // Not lvalue-convertible to int + struct C { + C(int v) : val(v) { } + operator int() && { return val; } + bool operator==(int b) const { return b == val; } + int val; + }; + using rvalue_input_range = test_range<C, input_iterator_wrapper_rval>; + do_test<rvalue_input_range, std::allocator<int>>(); + + return true; +} + +int main() +{ + test_ranges(); +} diff --git a/libstdc++-v3/testsuite/23_containers/forward_list/modifiers/prepend_range.cc b/libstdc++-v3/testsuite/23_containers/forward_list/modifiers/prepend_range.cc new file mode 100644 index 000000000000..ae38fb7e791f --- /dev/null +++ b/libstdc++-v3/testsuite/23_containers/forward_list/modifiers/prepend_range.cc @@ -0,0 +1,86 @@ +// { dg-do run { target c++23 } } + +#include <forward_list> +#include <span> +#include <testsuite_hooks.h> +#include <testsuite_iterators.h> +#include <testsuite_allocator.h> + +template<typename Range, typename Alloc> +constexpr void +do_test() +{ + // The forward_list's value_type. + using V = typename std::allocator_traits<Alloc>::value_type; + + // The range's value_type. + using T = std::ranges::range_value_t<Range>; + T a[]{1,2,3,4,5,6,7,8,9}; + + auto eq = [](const std::forward_list<V, Alloc>& l, std::span<T> r) { + if (std::distance(l.begin(), l.end()) != r.size()) + return false; + unsigned i = 0; + for (auto& e : l) + if (e != r[i++]) + return false; + return true; + }; + + std::forward_list<V, Alloc> v; + v.prepend_range(Range(a+4, a+9)); + VERIFY( eq(v, {a+4, 5}) ); + v.prepend_range(Range(a, a+4)); + VERIFY( eq(v, a) ); + v.prepend_range(Range(a, a)); + VERIFY( eq(v, a) ); + v.clear(); + v.prepend_range(Range(a, a)); + VERIFY( v.empty() ); +} + +template<typename Range> +void +do_test_a() +{ + do_test<Range, std::allocator<int>>(); + do_test<Range, __gnu_test::SimpleAllocator<int>>(); +} + +bool +test_ranges() +{ + using namespace __gnu_test; + + do_test_a<test_forward_range<int>>(); + do_test_a<test_forward_sized_range<int>>(); + do_test_a<test_sized_range_sized_sent<int, forward_iterator_wrapper>>(); + + do_test_a<test_input_range<int>>(); + do_test_a<test_input_sized_range<int>>(); + do_test_a<test_sized_range_sized_sent<int, input_iterator_wrapper>>(); + + do_test_a<test_range<int, input_iterator_wrapper_nocopy>>(); + do_test_a<test_sized_range<int, input_iterator_wrapper_nocopy>>(); + do_test_a<test_sized_range_sized_sent<int, input_iterator_wrapper_nocopy>>(); + + do_test_a<test_forward_range<short>>(); + do_test_a<test_input_range<short>>(); + + // Not lvalue-convertible to int + struct C { + C(int v) : val(v) { } + operator int() && { return val; } + bool operator==(int b) const { return b == val; } + int val; + }; + using rvalue_input_range = test_range<C, input_iterator_wrapper_rval>; + do_test<rvalue_input_range, std::allocator<int>>(); + + return true; +} + +int main() +{ + test_ranges(); +} diff --git a/libstdc++-v3/testsuite/23_containers/list/cons/from_range.cc b/libstdc++-v3/testsuite/23_containers/list/cons/from_range.cc new file mode 100644 index 000000000000..31448b9122b4 --- /dev/null +++ b/libstdc++-v3/testsuite/23_containers/list/cons/from_range.cc @@ -0,0 +1,100 @@ +// { dg-do run { target c++23 } } + +#include <list> +#include <span> +#include <testsuite_hooks.h> +#include <testsuite_iterators.h> +#include <testsuite_allocator.h> + +void +test_deduction_guide(long* p) +{ + __gnu_test::test_input_range<long> r(p, p); + std::list v(std::from_range, r); + static_assert(std::is_same_v<decltype(v), std::list<long>>); + + using Alloc = __gnu_test::SimpleAllocator<long>; + Alloc alloc; + std::list v2(std::from_range, r, alloc); + static_assert(std::is_same_v<decltype(v2), std::list<long, Alloc>>); +} + +template<typename Range, typename Alloc> +constexpr void +do_test(Alloc alloc) +{ + // The list's value_type. + using V = typename std::allocator_traits<Alloc>::value_type; + + // The range's value_type. + using T = std::ranges::range_value_t<Range>; + T a[]{1,2,3,4,5,6,7,8,9}; + + auto eq = [](const std::list<V, Alloc>& l, std::span<T> r) { + if (l.size() != r.size()) + return false; + unsigned i = 0; + for (auto& e : l) + if (e != r[i++]) + return false; + return true; + }; + + std::list<V, Alloc> v0(std::from_range, Range(a, a+0)); + VERIFY( v0.empty() ); + VERIFY( v0.get_allocator() == Alloc() ); + + std::list<V, Alloc> v4(std::from_range, Range(a, a+4)); + VERIFY( eq(v4, {a, 4}) ); + VERIFY( v4.get_allocator() == Alloc() ); + + std::list<V, Alloc> v9(std::from_range, Range(a, a+9), alloc); + VERIFY( eq(v9, {a, 9}) ); + VERIFY( v9.get_allocator() == alloc ); +} + +template<typename Range> +void +do_test_a() +{ + do_test<Range>(std::allocator<int>()); + do_test<Range>(__gnu_test::uneq_allocator<int>(42)); +} + +bool +test_ranges() +{ + using namespace __gnu_test; + + do_test_a<test_forward_range<int>>(); + do_test_a<test_forward_sized_range<int>>(); + do_test_a<test_sized_range_sized_sent<int, forward_iterator_wrapper>>(); + + do_test_a<test_input_range<int>>(); + do_test_a<test_input_sized_range<int>>(); + do_test_a<test_sized_range_sized_sent<int, input_iterator_wrapper>>(); + + do_test_a<test_range<int, input_iterator_wrapper_nocopy>>(); + do_test_a<test_sized_range<int, input_iterator_wrapper_nocopy>>(); + do_test_a<test_sized_range_sized_sent<int, input_iterator_wrapper_nocopy>>(); + + do_test_a<test_forward_range<short>>(); + do_test_a<test_input_range<short>>(); + + // Not lvalue-convertible to bool + struct C { + C(int v) : val(v) { } + operator int() && { return val; } + bool operator==(int b) const { return b == val; } + int val; + }; + using rvalue_input_range = test_range<C, input_iterator_wrapper_rval>; + do_test<rvalue_input_range>(std::allocator<int>()); + + return true; +} + +int main() +{ + test_ranges(); +} diff --git a/libstdc++-v3/testsuite/23_containers/list/modifiers/append_range.cc b/libstdc++-v3/testsuite/23_containers/list/modifiers/append_range.cc new file mode 100644 index 000000000000..9ddfc7dbea38 --- /dev/null +++ b/libstdc++-v3/testsuite/23_containers/list/modifiers/append_range.cc @@ -0,0 +1,86 @@ +// { dg-do run { target c++23 } } + +#include <list> +#include <span> +#include <testsuite_hooks.h> +#include <testsuite_iterators.h> +#include <testsuite_allocator.h> + +template<typename Range, typename Alloc> +constexpr void +do_test() +{ + // The list's value_type. + using V = typename std::allocator_traits<Alloc>::value_type; + + // The range's value_type. + using T = std::ranges::range_value_t<Range>; + T a[]{1,2,3,4,5,6,7,8,9}; + + auto eq = [](const std::list<V, Alloc>& l, std::span<T> r) { + if (l.size() != r.size()) + return false; + unsigned i = 0; + for (auto& e : l) + if (e != r[i++]) + return false; + return true; + }; + + std::list<V, Alloc> v; + v.append_range(Range(a, a+4)); + VERIFY( eq(v, {a, 4}) ); + v.append_range(Range(a+4, a+9)); + VERIFY( eq(v, a) ); + v.append_range(Range(a, a)); + VERIFY( eq(v, a) ); + v.clear(); + v.append_range(Range(a, a)); + VERIFY( v.empty() ); +} + +template<typename Range> +void +do_test_a() +{ + do_test<Range, std::allocator<int>>(); + do_test<Range, __gnu_test::SimpleAllocator<int>>(); +} + +bool +test_ranges() +{ + using namespace __gnu_test; + + do_test_a<test_forward_range<int>>(); + do_test_a<test_forward_sized_range<int>>(); + do_test_a<test_sized_range_sized_sent<int, forward_iterator_wrapper>>(); + + do_test_a<test_input_range<int>>(); + do_test_a<test_input_sized_range<int>>(); + do_test_a<test_sized_range_sized_sent<int, input_iterator_wrapper>>(); + + do_test_a<test_range<int, input_iterator_wrapper_nocopy>>(); + do_test_a<test_sized_range<int, input_iterator_wrapper_nocopy>>(); + do_test_a<test_sized_range_sized_sent<int, input_iterator_wrapper_nocopy>>(); + + do_test_a<test_forward_range<short>>(); + do_test_a<test_input_range<short>>(); + + // Not lvalue-convertible to int + struct C { + C(int v) : val(v) { } + operator int() && { return val; } + bool operator==(int b) const { return b == val; } + int val; + }; + using rvalue_input_range = test_range<C, input_iterator_wrapper_rval>; + do_test<rvalue_input_range, std::allocator<int>>(); + + return true; +} + +int main() +{ + test_ranges(); +} diff --git a/libstdc++-v3/testsuite/23_containers/list/modifiers/assign/assign_range.cc b/libstdc++-v3/testsuite/23_containers/list/modifiers/assign/assign_range.cc new file mode 100644 index 000000000000..84b9f07a8eb3 --- /dev/null +++ b/libstdc++-v3/testsuite/23_containers/list/modifiers/assign/assign_range.cc @@ -0,0 +1,99 @@ +// { dg-do run { target c++23 } } + +#include <list> +#include <span> +#include <testsuite_hooks.h> +#include <testsuite_iterators.h> +#include <testsuite_allocator.h> + +template<typename Range, typename Alloc> +constexpr void +do_test() +{ + // The list's value_type. + using V = typename std::allocator_traits<Alloc>::value_type; + + // The range's value_type. + using T = std::ranges::range_value_t<Range>; + T a[]{1,2,3,4,5,6,7,8,9}; + + auto eq = [](const std::list<V, Alloc>& l, std::span<T> r) { + if (l.size() != r.size()) + return false; + unsigned i = 0; + for (auto& e : l) + if (e != r[i++]) + return false; + return true; + }; + + // assign to empty list + std::list<V, Alloc> v; + v.assign_range(Range(a, a)); + VERIFY( v.empty() ); + v.assign_range(Range(a, a+4)); + VERIFY( eq(v, {a, 4}) ); + v.clear(); + v.assign_range(Range(a)); + VERIFY( eq(v, a) ); + + // assign to non-empty list + v.assign_range(Range(a, a+4)); + VERIFY( eq(v, {a, 4}) ); + v.assign_range(Range(a)); // larger than size() + VERIFY( eq(v, a) ); + v.assign_range(Range(a, a+4)); // smaller than size() + VERIFY( eq(v, {a, 4}) ); + v.clear(); + v.resize(4); + v.assign_range(Range(a, a+4)); // equal to size() + VERIFY( eq(v, {a, 4}) ); + v.assign_range(Range(a, a)); + VERIFY( v.empty() ); +} + +template<typename Range> +void +do_test_a() +{ + do_test<Range, std::allocator<int>>(); + do_test<Range, __gnu_test::SimpleAllocator<int>>(); +} + +bool +test_ranges() +{ + using namespace __gnu_test; + + do_test_a<test_forward_range<int>>(); + do_test_a<test_forward_sized_range<int>>(); + do_test_a<test_sized_range_sized_sent<int, forward_iterator_wrapper>>(); + + do_test_a<test_input_range<int>>(); + do_test_a<test_input_sized_range<int>>(); + do_test_a<test_sized_range_sized_sent<int, input_iterator_wrapper>>(); + + do_test_a<test_range<int, input_iterator_wrapper_nocopy>>(); + do_test_a<test_sized_range<int, input_iterator_wrapper_nocopy>>(); + do_test_a<test_sized_range_sized_sent<int, input_iterator_wrapper_nocopy>>(); + + do_test_a<test_forward_range<short>>(); + do_test_a<test_input_range<short>>(); + + // Not lvalue-convertible to int + struct C { + C(int v) : val(v) { } + operator int() && { return val; } + bool operator==(int b) const { return b == val; } + int val; + }; + using rvalue_input_range = test_range<C, input_iterator_wrapper_rval>; + do_test<rvalue_input_range, std::allocator<int>>(); + + return true; +} + +int main() +{ + test_ranges(); +} diff --git a/libstdc++-v3/testsuite/23_containers/list/modifiers/insert/insert_range.cc b/libstdc++-v3/testsuite/23_containers/list/modifiers/insert/insert_range.cc new file mode 100644 index 000000000000..a15d51d74668 --- /dev/null +++ b/libstdc++-v3/testsuite/23_containers/list/modifiers/insert/insert_range.cc @@ -0,0 +1,98 @@ +// { dg-do run { target c++23 } } + +#include <list> +#include <span> +#include <testsuite_hooks.h> +#include <testsuite_iterators.h> +#include <testsuite_allocator.h> + +template<typename Range, typename Alloc> +constexpr void +do_test() +{ + // The list's value_type. + using V = typename std::allocator_traits<Alloc>::value_type; + + // The range's value_type. + using T = std::ranges::range_value_t<Range>; + T a[]{1,2,3,4,5,6,7,8,9}; + + auto eq = [](const std::list<V, Alloc>& l, std::span<T> r) { + if (l.size() != r.size()) + return false; + unsigned i = 0; + for (auto& e : l) + if (e != r[i++]) + return false; + return true; + }; + + std::list<V, Alloc> v; + v.insert_range(v.begin(), Range(a, a)); + VERIFY( v.empty() ); + v.insert_range(v.begin(), Range(a, a+4)); + VERIFY( eq(v, {a, a+4}) ); + v.clear(); + v.insert_range(v.begin(), Range(a+4, a+9)); + VERIFY( eq(v, {a+4, a+9}) ); + v.insert_range(v.begin(), Range(a, a+4)); + VERIFY( eq(v, a) ); + v.clear(); + v.insert_range(v.begin(), Range(a, a+3)); + v.insert_range(v.end(), Range(a+6, a+9)); + v.insert_range(std::next(v.begin(), 3), Range(a+3, a+6)); + VERIFY( eq(v, a) ); + v.resize(3); + v.insert_range(std::next(v.begin()), Range(a+4, a+9)); + v.insert_range(std::next(v.begin()), Range(a+1, a+4)); + v.resize(9); + VERIFY( eq(v, a) ); + v.insert_range(std::next(v.begin(), 6), Range(a, a)); + VERIFY( eq(v, a) ); +} + +template<typename Range> +void +do_test_a() +{ + do_test<Range, std::allocator<int>>(); + do_test<Range, __gnu_test::SimpleAllocator<int>>(); +} + +bool +test_ranges() +{ + using namespace __gnu_test; + + do_test_a<test_forward_range<int>>(); + do_test_a<test_forward_sized_range<int>>(); + do_test_a<test_sized_range_sized_sent<int, forward_iterator_wrapper>>(); + + do_test_a<test_input_range<int>>(); + do_test_a<test_input_sized_range<int>>(); + do_test_a<test_sized_range_sized_sent<int, input_iterator_wrapper>>(); + + do_test_a<test_range<int, input_iterator_wrapper_nocopy>>(); + do_test_a<test_sized_range<int, input_iterator_wrapper_nocopy>>(); + do_test_a<test_sized_range_sized_sent<int, input_iterator_wrapper_nocopy>>(); + + do_test_a<test_forward_range<short>>(); + do_test_a<test_input_range<short>>(); + + // Not lvalue-convertible to int + struct C { + C(int v) : val(v) { } + operator int() && { return val; } + bool operator==(int b) const { return b == val; } + int val; + }; + using rvalue_input_range = test_range<C, input_iterator_wrapper_rval>; + do_test<rvalue_input_range, std::allocator<int>>(); + + return true; +} + +int main() +{ + test_ranges(); +} diff --git a/libstdc++-v3/testsuite/23_containers/list/modifiers/prepend_range.cc b/libstdc++-v3/testsuite/23_containers/list/modifiers/prepend_range.cc new file mode 100644 index 000000000000..fe217e195731 --- /dev/null +++ b/libstdc++-v3/testsuite/23_containers/list/modifiers/prepend_range.cc @@ -0,0 +1,86 @@ +// { dg-do run { target c++23 } } + +#include <list> +#include <span> +#include <testsuite_hooks.h> +#include <testsuite_iterators.h> +#include <testsuite_allocator.h> + +template<typename Range, typename Alloc> +constexpr void +do_test() +{ + // The list's value_type. + using V = typename std::allocator_traits<Alloc>::value_type; + + // The range's value_type. + using T = std::ranges::range_value_t<Range>; + T a[]{1,2,3,4,5,6,7,8,9}; + + auto eq = [](const std::list<V, Alloc>& l, std::span<T> r) { + if (l.size() != r.size()) + return false; + unsigned i = 0; + for (auto& e : l) + if (e != r[i++]) + return false; + return true; + }; + + std::list<V, Alloc> v; + v.prepend_range(Range(a+4, a+9)); + VERIFY( eq(v, {a+4, 5}) ); + v.prepend_range(Range(a, a+4)); + VERIFY( eq(v, a) ); + v.prepend_range(Range(a, a)); + VERIFY( eq(v, a) ); + v.clear(); + v.prepend_range(Range(a, a)); + VERIFY( v.empty() ); +} + +template<typename Range> +void +do_test_a() +{ + do_test<Range, std::allocator<int>>(); + do_test<Range, __gnu_test::SimpleAllocator<int>>(); +} + +bool +test_ranges() +{ + using namespace __gnu_test; + + do_test_a<test_forward_range<int>>(); + do_test_a<test_forward_sized_range<int>>(); + do_test_a<test_sized_range_sized_sent<int, forward_iterator_wrapper>>(); + + do_test_a<test_input_range<int>>(); + do_test_a<test_input_sized_range<int>>(); + do_test_a<test_sized_range_sized_sent<int, input_iterator_wrapper>>(); + + do_test_a<test_range<int, input_iterator_wrapper_nocopy>>(); + do_test_a<test_sized_range<int, input_iterator_wrapper_nocopy>>(); + do_test_a<test_sized_range_sized_sent<int, input_iterator_wrapper_nocopy>>(); + + do_test_a<test_forward_range<short>>(); + do_test_a<test_input_range<short>>(); + + // Not lvalue-convertible to int + struct C { + C(int v) : val(v) { } + operator int() && { return val; } + bool operator==(int b) const { return b == val; } + int val; + }; + using rvalue_input_range = test_range<C, input_iterator_wrapper_rval>; + do_test<rvalue_input_range, std::allocator<int>>(); + + return true; +} + +int main() +{ + test_ranges(); +}