* [PATCH] PR libstdc++/91620 Implement DR 526 for std::[forward_]list::remove_if/unique @ 2019-12-27 15:54 François Dumont 2020-03-03 5:42 ` François Dumont ` (2 more replies) 0 siblings, 3 replies; 7+ messages in thread From: François Dumont @ 2019-12-27 15:54 UTC (permalink / raw) To: libstdc++, gcc-patches [-- Attachment #1: Type: text/plain, Size: 1072 bytes --] Here is the patch to extend DR 526 to forward_list and list remove_if and unique. As the adopted pattern is simpler I also applied it to the remove methods.    PR libstdc++/91620    * include/bits/forward_list.tcc (forward_list<>::remove): Collect nodes    to destroy in an intermediate forward_list.    (forward_list<>::remove_if, forward_list<>::unique): Likewise.    * include/bits/list.tcc (list<>::remove, list<>::unique): Likewise.    (list<>::remove_if): Likewise.    * include/debug/forward_list (forward_list<>::_M_erase_after): Remove.    (forward_list<>::erase_after): Adapt.    (forward_list<>::remove, forward_list<>::remove_if): Collect nodes to    destroy in an intermediate forward_list.    (forward_list<>::unique): Likewise.    * include/debug/list (list<>::remove, list<>::unique): Likewise.    (list<>::remove_if): Likewise. Tested under Linux x86_64 normal and debug modes. Ok to commit ? François [-- Attachment #2: 91620.patch --] [-- Type: text/x-patch, Size: 14757 bytes --] diff --git a/libstdc++-v3/include/bits/forward_list.tcc b/libstdc++-v3/include/bits/forward_list.tcc index 088111e3330..70de7e75a43 100644 --- a/libstdc++-v3/include/bits/forward_list.tcc +++ b/libstdc++-v3/include/bits/forward_list.tcc @@ -290,30 +290,19 @@ _GLIBCXX_BEGIN_NAMESPACE_CONTAINER remove(const _Tp& __val) -> __remove_return_type { size_type __removed __attribute__((__unused__)) = 0; - _Node_base* __curr = &this->_M_impl._M_head; - _Node_base* __extra = nullptr; + forward_list __to_destroy(get_allocator()); - while (_Node* __tmp = static_cast<_Node*>(__curr->_M_next)) - { + auto __prev_it = cbefore_begin(); + while (_Node* __tmp = static_cast<_Node*>(__prev_it._M_node->_M_next)) if (*__tmp->_M_valptr() == __val) { - if (__tmp->_M_valptr() != std::__addressof(__val)) - { - this->_M_erase_after(__curr); + __to_destroy.splice_after(__to_destroy.cbefore_begin(), + *this, __prev_it); _GLIBCXX20_ONLY( __removed++ ); - continue; } else - __extra = __curr; - } - __curr = __curr->_M_next; - } + ++__prev_it; - if (__extra) - { - this->_M_erase_after(__extra); - _GLIBCXX20_ONLY( __removed++ ); - } return _GLIBCXX20_ONLY( __removed ); } @@ -324,17 +313,19 @@ _GLIBCXX_BEGIN_NAMESPACE_CONTAINER remove_if(_Pred __pred) -> __remove_return_type { size_type __removed __attribute__((__unused__)) = 0; - _Node_base* __curr = &this->_M_impl._M_head; - while (_Node* __tmp = static_cast<_Node*>(__curr->_M_next)) - { + forward_list __to_destroy(get_allocator()); + + auto __prev_it = cbefore_begin(); + while (_Node* __tmp = static_cast<_Node*>(__prev_it._M_node->_M_next)) if (__pred(*__tmp->_M_valptr())) { - this->_M_erase_after(__curr); + __to_destroy.splice_after(__to_destroy.cbefore_begin(), + *this, __prev_it); _GLIBCXX20_ONLY( __removed++ ); } else - __curr = __curr->_M_next; - } + ++__prev_it; + return _GLIBCXX20_ONLY( __removed ); } @@ -348,19 +339,23 @@ _GLIBCXX_BEGIN_NAMESPACE_CONTAINER iterator __last = end(); if (__first == __last) return _GLIBCXX20_ONLY(0); + + forward_list __to_destroy(get_allocator()); size_type __removed __attribute__((__unused__)) = 0; iterator __next = __first; while (++__next != __last) { if (__binary_pred(*__first, *__next)) { - erase_after(__first); + __to_destroy.splice_after(__to_destroy.cbefore_begin(), + *this, __first); _GLIBCXX20_ONLY( __removed++ ); } else __first = __next; __next = __first; } + return _GLIBCXX20_ONLY( __removed ); } diff --git a/libstdc++-v3/include/bits/list.tcc b/libstdc++-v3/include/bits/list.tcc index 0ac6654b079..5a6fd5b0824 100644 --- a/libstdc++-v3/include/bits/list.tcc +++ b/libstdc++-v3/include/bits/list.tcc @@ -331,10 +331,12 @@ _GLIBCXX_BEGIN_NAMESPACE_CONTAINER list<_Tp, _Alloc>:: remove(const value_type& __value) { +#if !_GLIBCXX_USE_CXX11_ABI size_type __removed __attribute__((__unused__)) = 0; +#endif + list __to_destroy(get_allocator()); iterator __first = begin(); iterator __last = end(); - iterator __extra = __last; while (__first != __last) { iterator __next = __first; @@ -344,22 +346,20 @@ _GLIBCXX_BEGIN_NAMESPACE_CONTAINER // _GLIBCXX_RESOLVE_LIB_DEFECTS // 526. Is it undefined if a function in the standard changes // in parameters? - if (std::__addressof(*__first) != std::__addressof(__value)) - { - _M_erase(__first); + __to_destroy.splice(__to_destroy.begin(), *this, __first); +#if !_GLIBCXX_USE_CXX11_ABI _GLIBCXX20_ONLY( __removed++ ); +#endif } - else - __extra = __first; - } + __first = __next; } - if (__extra != __last) - { - _M_erase(__extra); - _GLIBCXX20_ONLY( __removed++ ); - } + +#if !_GLIBCXX_USE_CXX11_ABI return _GLIBCXX20_ONLY( __removed ); +#else + return _GLIBCXX20_ONLY( __to_destroy.size() ); +#endif } template<typename _Tp, typename _Alloc> @@ -371,20 +371,30 @@ _GLIBCXX_BEGIN_NAMESPACE_CONTAINER iterator __last = end(); if (__first == __last) return _GLIBCXX20_ONLY( 0 ); +#if !_GLIBCXX_USE_CXX11_ABI size_type __removed __attribute__((__unused__)) = 0; +#endif + list __to_destroy(get_allocator()); iterator __next = __first; while (++__next != __last) { if (*__first == *__next) { - _M_erase(__next); + __to_destroy.splice(__to_destroy.begin(), *this, __next); +#if !_GLIBCXX_USE_CXX11_ABI _GLIBCXX20_ONLY( __removed++ ); +#endif } else __first = __next; __next = __first; } + +#if !_GLIBCXX_USE_CXX11_ABI return _GLIBCXX20_ONLY( __removed ); +#else + return _GLIBCXX20_ONLY( __to_destroy.size() ); +#endif } template<typename _Tp, typename _Alloc> @@ -533,7 +543,10 @@ _GLIBCXX_BEGIN_NAMESPACE_CONTAINER list<_Tp, _Alloc>:: remove_if(_Predicate __pred) { +#if !_GLIBCXX_USE_CXX11_ABI size_type __removed __attribute__((__unused__)) = 0; +#endif + list __to_destroy(get_allocator()); iterator __first = begin(); iterator __last = end(); while (__first != __last) @@ -542,12 +555,19 @@ _GLIBCXX_BEGIN_NAMESPACE_CONTAINER ++__next; if (__pred(*__first)) { - _M_erase(__first); + __to_destroy.splice(__to_destroy.begin(), *this, __first); +#if !_GLIBCXX_USE_CXX11_ABI _GLIBCXX20_ONLY( __removed++ ); +#endif } __first = __next; } + +#if !_GLIBCXX_USE_CXX11_ABI return _GLIBCXX20_ONLY( __removed ); +#else + return _GLIBCXX20_ONLY( __to_destroy.size() ); +#endif } template<typename _Tp, typename _Alloc> @@ -560,20 +580,30 @@ _GLIBCXX_BEGIN_NAMESPACE_CONTAINER iterator __last = end(); if (__first == __last) return _GLIBCXX20_ONLY(0); +#if !_GLIBCXX_USE_CXX11_ABI size_type __removed __attribute__((__unused__)) = 0; +#endif + list __to_destroy(get_allocator()); iterator __next = __first; while (++__next != __last) { if (__binary_pred(*__first, *__next)) { - _M_erase(__next); + __to_destroy.splice(__to_destroy.begin(), *this, __next); +#if !_GLIBCXX_USE_CXX11_ABI _GLIBCXX20_ONLY( __removed++ ); +#endif } else __first = __next; __next = __first; } + +#if !_GLIBCXX_USE_CXX11_ABI return _GLIBCXX20_ONLY( __removed ); +#else + return _GLIBCXX20_ONLY( __to_destroy.size() ); +#endif } #undef _GLIBCXX20_ONLY diff --git a/libstdc++-v3/include/debug/forward_list b/libstdc++-v3/include/debug/forward_list index f1756ddec9d..9889eb9da83 100644 --- a/libstdc++-v3/include/debug/forward_list +++ b/libstdc++-v3/include/debug/forward_list @@ -442,21 +442,15 @@ namespace __debug return { _Base::insert_after(__pos.base(), __il), this }; } - private: - _Base_iterator - _M_erase_after(_Base_const_iterator __pos) - { - _Base_const_iterator __next = std::next(__pos); - this->_M_invalidate_if([__next](_Base_const_iterator __it) - { return __it == __next; }); - return _Base::erase_after(__pos); - } - public: iterator erase_after(const_iterator __pos) { __glibcxx_check_erase_after(__pos); - return { _M_erase_after(__pos.base()), this }; + + _Base_const_iterator __next = std::next(__pos.base()); + this->_M_invalidate_if([__next](_Base_const_iterator __it) + { return __it == __next; }); + return { _Base::erase_after(__pos.base()), this }; } iterator @@ -681,29 +675,23 @@ namespace __debug return _Base::remove(__val); size_type __removed __attribute__((__unused__)) = 0; - _Base_iterator __x = _Base::before_begin(); - _Base_iterator __old = __x++; - _Base_iterator __extra = _Base::end(); - while (__x != _Base::end()) + _Base __to_destroy(get_allocator()); + _Base_const_iterator __x = _Base::cbefore_begin(); + _Base_const_iterator __old = __x++; + while (__x != _Base::cend()) { if (*__x == __val) { - if (std::__addressof(*__x) != std::__addressof(__val)) - { - __x = _M_erase_after(__old); + _Base_const_iterator __next = std::next(__old); + this->_M_invalidate_if([__next](_Base_const_iterator __it) + { return __it == __next; }); + __to_destroy.splice_after(__to_destroy.cbefore_begin(), + _M_base(), __old); + __x = __old; _GLIBCXX20_ONLY( __removed++ ); - continue; - } - else - __extra = __old; - } - __old = __x++; } - if (__extra != _Base::end()) - { - this->_M_erase_after(__extra); - _GLIBCXX20_ONLY( __removed++ ); + __old = __x++; } return _GLIBCXX20_ONLY( __removed ); @@ -717,16 +705,23 @@ namespace __debug return _Base::remove_if(__pred); size_type __removed __attribute__((__unused__)) = 0; + _Base __to_destroy(get_allocator()); _Base_iterator __x = _Base::before_begin(); _Base_iterator __old = __x++; while (__x != _Base::end()) + { if (__pred(*__x)) { - __x = _M_erase_after(__old); + this->_M_invalidate_if([__x](_Base_const_iterator __it) + { return __it == __x; }); + __to_destroy.splice_after(__to_destroy.cbefore_begin(), + _M_base(), __old); + __x = __old; _GLIBCXX20_ONLY( __removed++ ); } - else + __old = __x++; + } return _GLIBCXX20_ONLY( __removed ); } @@ -743,21 +738,26 @@ namespace __debug if (!this->_M_iterators && !this->_M_const_iterators) return _Base::unique(__binary_pred); - _Base_iterator __first = _Base::begin(); - _Base_iterator __last = _Base::end(); + _Base_const_iterator __first = _Base::cbegin(); + _Base_const_iterator __last = _Base::cend(); if (__first == __last) return _GLIBCXX20_ONLY(0); size_type __removed __attribute__((__unused__)) = 0; - _Base_iterator __next = std::next(__first); + _Base __to_destroy(get_allocator()); + _Base_const_iterator __next = std::next(__first); while (__next != __last) { if (__binary_pred(*__first, *__next)) { - __next = _M_erase_after(__first); + this->_M_invalidate_if([__next](_Base_const_iterator __it) + { return __it == __next; }); + __to_destroy.splice_after(__to_destroy.cbefore_begin(), + _M_base(), __first); + __next = __first; _GLIBCXX20_ONLY( __removed++ ); } - else + __first = __next++; } diff --git a/libstdc++-v3/include/debug/list b/libstdc++-v3/include/debug/list index 140546a633e..bc4895405d1 100644 --- a/libstdc++-v3/include/debug/list +++ b/libstdc++-v3/include/debug/list @@ -671,36 +671,36 @@ namespace __debug if (!this->_M_iterators && !this->_M_const_iterators) return _Base::remove(__value); +#if !_GLIBCXX_USE_CXX11_ABI size_type __removed __attribute__((__unused__)) = 0; +#endif + _Base __to_destroy(get_allocator()); _Base_iterator __first = _Base::begin(); _Base_iterator __last = _Base::end(); - _Base_iterator __extra = __last; while (__first != __last) { + _Base_iterator __next = __first; + ++__next; if (*__first == __value) + { // _GLIBCXX_RESOLVE_LIB_DEFECTS // 526. Is it undefined if a function in the standard changes // in parameters? - if (std::__addressof(*__first) != std::__addressof(__value)) - { - __first = _M_erase(__first); + this->_M_invalidate_if(_Equal(__first)); + __to_destroy.splice(__to_destroy.begin(), _M_base(), __first); +#if !_GLIBCXX_USE_CXX11_ABI _GLIBCXX20_ONLY( __removed++ ); - } - else - { - __extra = __first; - ++__first; - } - else - ++__first; +#endif } - if (__extra != __last) - { - _M_erase(__extra); - _GLIBCXX20_ONLY( __removed++ ); + __first = __next; } + +#if !_GLIBCXX_USE_CXX11_ABI return _GLIBCXX20_ONLY( __removed ); +#else + return _GLIBCXX20_ONLY( __to_destroy.size() ); +#endif } template<class _Predicate> @@ -710,17 +710,31 @@ namespace __debug if (!this->_M_iterators && !this->_M_const_iterators) return _Base::remove_if(__pred); +#if !_GLIBCXX_USE_CXX11_ABI size_type __removed __attribute__((__unused__)) = 0; +#endif + _Base __to_destroy(get_allocator()); for (_Base_iterator __x = _Base::begin(); __x != _Base::end(); ) + { + _Base_iterator __next = __x; + ++__next; if (__pred(*__x)) { - __x = _M_erase(__x); + this->_M_invalidate_if(_Equal(__x)); + __to_destroy.splice(__to_destroy.begin(), _M_base(), __x); +#if !_GLIBCXX_USE_CXX11_ABI _GLIBCXX20_ONLY( __removed++ ); +#endif + } + + __x = __next; } - else - ++__x; +#if !_GLIBCXX_USE_CXX11_ABI return _GLIBCXX20_ONLY( __removed ); +#else + return _GLIBCXX20_ONLY( __to_destroy.size() ); +#endif } _GLIBCXX_LIST_REMOVE_RETURN_TYPE_TAG @@ -733,21 +747,31 @@ namespace __debug if (empty()) return _GLIBCXX20_ONLY(0); +#if !_GLIBCXX_USE_CXX11_ABI size_type __removed __attribute__((__unused__)) = 0; +#endif + _Base __to_destroy(get_allocator()); _Base_iterator __first = _Base::begin(); _Base_iterator __last = _Base::end(); _Base_iterator __next = __first; while (++__next != __last) if (*__first == *__next) { - _M_erase(__next); + this->_M_invalidate_if(_Equal(__next)); + __to_destroy.splice(__to_destroy.begin(), _M_base(), __next); __next = __first; +#if !_GLIBCXX_USE_CXX11_ABI _GLIBCXX20_ONLY( __removed++ ); +#endif } else __first = __next; +#if !_GLIBCXX_USE_CXX11_ABI return _GLIBCXX20_ONLY( __removed ); +#else + return _GLIBCXX20_ONLY( __to_destroy.size() ); +#endif } template<class _BinaryPredicate> @@ -760,21 +784,32 @@ namespace __debug if (empty()) return _GLIBCXX20_ONLY(0); + +#if !_GLIBCXX_USE_CXX11_ABI size_type __removed __attribute__((__unused__)) = 0; +#endif + _Base __to_destroy(get_allocator()); _Base_iterator __first = _Base::begin(); _Base_iterator __last = _Base::end(); - _Base_iterator __next = __first;; + _Base_iterator __next = __first; while (++__next != __last) if (__binary_pred(*__first, *__next)) { - _M_erase(__next); + this->_M_invalidate_if(_Equal(__next)); + __to_destroy.splice(__to_destroy.begin(), _M_base(), __next); __next = __first; +#if !_GLIBCXX_USE_CXX11_ABI _GLIBCXX20_ONLY( __removed++ ); +#endif } else __first = __next; +#if !_GLIBCXX_USE_CXX11_ABI return _GLIBCXX20_ONLY( __removed ); +#else + return _GLIBCXX20_ONLY( __to_destroy.size() ); +#endif } #undef _GLIBCXX_LIST_REMOVE_RETURN_TYPE_TAG ^ permalink raw reply [flat|nested] 7+ messages in thread
* Re: [PATCH] PR libstdc++/91620 Implement DR 526 for std::[forward_]list::remove_if/unique 2019-12-27 15:54 [PATCH] PR libstdc++/91620 Implement DR 526 for std::[forward_]list::remove_if/unique François Dumont @ 2020-03-03 5:42 ` François Dumont 2020-03-03 21:33 ` Jonathan Wakely 2020-08-10 19:07 ` François Dumont 2020-08-11 9:10 ` Jonathan Wakely 2 siblings, 1 reply; 7+ messages in thread From: François Dumont @ 2020-03-03 5:42 UTC (permalink / raw) To: libstdc++, gcc-patches Hi Isn't it something to fix before gcc 10 release ? François On 12/27/19 11:57 AM, François Dumont wrote: > Here is the patch to extend DR 526 to forward_list and list remove_if > and unique. > > As the adopted pattern is simpler I also applied it to the remove > methods. > > PR libstdc++/91620 > * include/bits/forward_list.tcc (forward_list<>::remove): Collect > nodes > to destroy in an intermediate forward_list. > (forward_list<>::remove_if, forward_list<>::unique): Likewise. > * include/bits/list.tcc (list<>::remove, list<>::unique): Likewise. > (list<>::remove_if): Likewise. > * include/debug/forward_list (forward_list<>::_M_erase_after): > Remove. > (forward_list<>::erase_after): Adapt. > (forward_list<>::remove, forward_list<>::remove_if): Collect nodes to > destroy in an intermediate forward_list. > (forward_list<>::unique): Likewise. > * include/debug/list (list<>::remove, list<>::unique): Likewise. > (list<>::remove_if): Likewise. > > Tested under Linux x86_64 normal and debug modes. > > Ok to commit ? > > François > ^ permalink raw reply [flat|nested] 7+ messages in thread
* Re: [PATCH] PR libstdc++/91620 Implement DR 526 for std::[forward_]list::remove_if/unique 2020-03-03 5:42 ` François Dumont @ 2020-03-03 21:33 ` Jonathan Wakely 2020-03-03 21:37 ` Jonathan Wakely 0 siblings, 1 reply; 7+ messages in thread From: Jonathan Wakely @ 2020-03-03 21:33 UTC (permalink / raw) To: François Dumont; +Cc: libstdc++, gcc-patches On 03/03/20 06:42 +0100, François Dumont wrote: >Hi > > Isn't it something to fix before gcc 10 release ? No, I don't think so. It's not a regression. ^ permalink raw reply [flat|nested] 7+ messages in thread
* Re: [PATCH] PR libstdc++/91620 Implement DR 526 for std::[forward_]list::remove_if/unique 2020-03-03 21:33 ` Jonathan Wakely @ 2020-03-03 21:37 ` Jonathan Wakely 0 siblings, 0 replies; 7+ messages in thread From: Jonathan Wakely @ 2020-03-03 21:37 UTC (permalink / raw) To: François Dumont; +Cc: libstdc++, gcc-patches On 03/03/20 21:32 +0000, Jonathan Wakely wrote: >On 03/03/20 06:42 +0100, François Dumont wrote: >>Hi >> >> Isn't it something to fix before gcc 10 release ? > >No, I don't think so. It's not a regression. (And is not experimental C++20 stuff, and is not just changing tests or docs). ^ permalink raw reply [flat|nested] 7+ messages in thread
* Re: [PATCH] PR libstdc++/91620 Implement DR 526 for std::[forward_]list::remove_if/unique 2019-12-27 15:54 [PATCH] PR libstdc++/91620 Implement DR 526 for std::[forward_]list::remove_if/unique François Dumont 2020-03-03 5:42 ` François Dumont @ 2020-08-10 19:07 ` François Dumont 2020-08-11 9:10 ` Jonathan Wakely 2 siblings, 0 replies; 7+ messages in thread From: François Dumont @ 2020-08-10 19:07 UTC (permalink / raw) To: libstdc++, gcc-patches [-- Attachment #1: Type: text/plain, Size: 1522 bytes --] Gentle reminder, this time with tests. I've added one for list::remove cause I think there was none, for forward_list we had remove_freed.cc. I added // { dg-options "-g -O0" } in the new tests otherwise it doesn't fail, that's life with UB. I know that it can pass also with those options. If you prefer we can go without it and let Valgrind detect the issue. Whatever, once the patch is in place it doesn't fail anymore. François On 27/12/19 11:57 am, François Dumont wrote: > Here is the patch to extend DR 526 to forward_list and list remove_if > and unique. > > As the adopted pattern is simpler I also applied it to the remove > methods. > > PR libstdc++/91620 > * include/bits/forward_list.tcc (forward_list<>::remove): Collect > nodes > to destroy in an intermediate forward_list. > (forward_list<>::remove_if, forward_list<>::unique): Likewise. > * include/bits/list.tcc (list<>::remove, list<>::unique): Likewise. > (list<>::remove_if): Likewise. > * include/debug/forward_list (forward_list<>::_M_erase_after): > Remove. > (forward_list<>::erase_after): Adapt. > (forward_list<>::remove, forward_list<>::remove_if): Collect nodes to > destroy in an intermediate forward_list. > (forward_list<>::unique): Likewise. > * include/debug/list (list<>::remove, list<>::unique): Likewise. > (list<>::remove_if): Likewise. > > Tested under Linux x86_64 normal and debug modes. > > Ok to commit ? > > François > [-- Attachment #2: 91620.patch --] [-- Type: text/x-patch, Size: 21099 bytes --] diff --git a/libstdc++-v3/include/bits/forward_list.tcc b/libstdc++-v3/include/bits/forward_list.tcc index c42bdc0fd13..3f94066bd55 100644 --- a/libstdc++-v3/include/bits/forward_list.tcc +++ b/libstdc++-v3/include/bits/forward_list.tcc @@ -290,30 +290,19 @@ _GLIBCXX_BEGIN_NAMESPACE_CONTAINER remove(const _Tp& __val) -> __remove_return_type { size_type __removed __attribute__((__unused__)) = 0; - _Node_base* __curr = &this->_M_impl._M_head; - _Node_base* __extra = nullptr; + forward_list __to_destroy(get_allocator()); - while (_Node* __tmp = static_cast<_Node*>(__curr->_M_next)) - { - if (*__tmp->_M_valptr() == __val) - { - if (__tmp->_M_valptr() != std::__addressof(__val)) - { - this->_M_erase_after(__curr); - _GLIBCXX20_ONLY( __removed++ ); - continue; - } - else - __extra = __curr; - } - __curr = __curr->_M_next; - } + auto __prev_it = cbefore_begin(); + while (_Node* __tmp = static_cast<_Node*>(__prev_it._M_node->_M_next)) + if (*__tmp->_M_valptr() == __val) + { + __to_destroy.splice_after(__to_destroy.cbefore_begin(), + *this, __prev_it); + _GLIBCXX20_ONLY( __removed++ ); + } + else + ++__prev_it; - if (__extra) - { - this->_M_erase_after(__extra); - _GLIBCXX20_ONLY( __removed++ ); - } return _GLIBCXX20_ONLY( __removed ); } @@ -324,17 +313,19 @@ _GLIBCXX_BEGIN_NAMESPACE_CONTAINER remove_if(_Pred __pred) -> __remove_return_type { size_type __removed __attribute__((__unused__)) = 0; - _Node_base* __curr = &this->_M_impl._M_head; - while (_Node* __tmp = static_cast<_Node*>(__curr->_M_next)) - { - if (__pred(*__tmp->_M_valptr())) - { - this->_M_erase_after(__curr); - _GLIBCXX20_ONLY( __removed++ ); - } - else - __curr = __curr->_M_next; - } + forward_list __to_destroy(get_allocator()); + + auto __prev_it = cbefore_begin(); + while (_Node* __tmp = static_cast<_Node*>(__prev_it._M_node->_M_next)) + if (__pred(*__tmp->_M_valptr())) + { + __to_destroy.splice_after(__to_destroy.cbefore_begin(), + *this, __prev_it); + _GLIBCXX20_ONLY( __removed++ ); + } + else + ++__prev_it; + return _GLIBCXX20_ONLY( __removed ); } @@ -348,20 +339,24 @@ _GLIBCXX_BEGIN_NAMESPACE_CONTAINER iterator __last = end(); if (__first == __last) return _GLIBCXX20_ONLY(0); + + forward_list __to_destroy(get_allocator()); size_type __removed __attribute__((__unused__)) = 0; iterator __next = __first; while (++__next != __last) { if (__binary_pred(*__first, *__next)) { - erase_after(__first); + __to_destroy.splice_after(__to_destroy.cbefore_begin(), + *this, __first); _GLIBCXX20_ONLY( __removed++ ); } else __first = __next; __next = __first; } - return _GLIBCXX20_ONLY( __removed ); + + return _GLIBCXX20_ONLY( __removed ); } #undef _GLIBCXX20_ONLY diff --git a/libstdc++-v3/include/bits/list.tcc b/libstdc++-v3/include/bits/list.tcc index ce9e983c539..9b664f11454 100644 --- a/libstdc++-v3/include/bits/list.tcc +++ b/libstdc++-v3/include/bits/list.tcc @@ -331,10 +331,12 @@ _GLIBCXX_BEGIN_NAMESPACE_CONTAINER list<_Tp, _Alloc>:: remove(const value_type& __value) { +#if !_GLIBCXX_USE_CXX11_ABI size_type __removed __attribute__((__unused__)) = 0; +#endif + list __to_destroy(get_allocator()); iterator __first = begin(); iterator __last = end(); - iterator __extra = __last; while (__first != __last) { iterator __next = __first; @@ -344,22 +346,20 @@ _GLIBCXX_BEGIN_NAMESPACE_CONTAINER // _GLIBCXX_RESOLVE_LIB_DEFECTS // 526. Is it undefined if a function in the standard changes // in parameters? - if (std::__addressof(*__first) != std::__addressof(__value)) - { - _M_erase(__first); - _GLIBCXX20_ONLY( __removed++ ); - } - else - __extra = __first; + __to_destroy.splice(__to_destroy.begin(), *this, __first); +#if !_GLIBCXX_USE_CXX11_ABI + _GLIBCXX20_ONLY( __removed++ ); +#endif } + __first = __next; } - if (__extra != __last) - { - _M_erase(__extra); - _GLIBCXX20_ONLY( __removed++ ); - } - return _GLIBCXX20_ONLY( __removed ); + +#if !_GLIBCXX_USE_CXX11_ABI + return _GLIBCXX20_ONLY( __removed ); +#else + return _GLIBCXX20_ONLY( __to_destroy.size() ); +#endif } template<typename _Tp, typename _Alloc> @@ -371,20 +371,30 @@ _GLIBCXX_BEGIN_NAMESPACE_CONTAINER iterator __last = end(); if (__first == __last) return _GLIBCXX20_ONLY( 0 ); +#if !_GLIBCXX_USE_CXX11_ABI size_type __removed __attribute__((__unused__)) = 0; +#endif + list __to_destroy(get_allocator()); iterator __next = __first; while (++__next != __last) { if (*__first == *__next) { - _M_erase(__next); + __to_destroy.splice(__to_destroy.begin(), *this, __next); +#if !_GLIBCXX_USE_CXX11_ABI _GLIBCXX20_ONLY( __removed++ ); +#endif } else __first = __next; __next = __first; } + +#if !_GLIBCXX_USE_CXX11_ABI return _GLIBCXX20_ONLY( __removed ); +#else + return _GLIBCXX20_ONLY( __to_destroy.size() ); +#endif } template<typename _Tp, typename _Alloc> @@ -533,21 +543,31 @@ _GLIBCXX_BEGIN_NAMESPACE_CONTAINER list<_Tp, _Alloc>:: remove_if(_Predicate __pred) { +#if !_GLIBCXX_USE_CXX11_ABI size_type __removed __attribute__((__unused__)) = 0; - iterator __first = begin(); - iterator __last = end(); - while (__first != __last) +#endif + list __to_destroy(get_allocator()); + iterator __first = begin(); + iterator __last = end(); + while (__first != __last) { iterator __next = __first; ++__next; if (__pred(*__first)) { - _M_erase(__first); + __to_destroy.splice(__to_destroy.begin(), *this, __first); +#if !_GLIBCXX_USE_CXX11_ABI _GLIBCXX20_ONLY( __removed++ ); +#endif } __first = __next; } + +#if !_GLIBCXX_USE_CXX11_ABI return _GLIBCXX20_ONLY( __removed ); +#else + return _GLIBCXX20_ONLY( __to_destroy.size() ); +#endif } template<typename _Tp, typename _Alloc> @@ -560,20 +580,30 @@ _GLIBCXX_BEGIN_NAMESPACE_CONTAINER iterator __last = end(); if (__first == __last) return _GLIBCXX20_ONLY(0); +#if !_GLIBCXX_USE_CXX11_ABI size_type __removed __attribute__((__unused__)) = 0; +#endif + list __to_destroy(get_allocator()); iterator __next = __first; while (++__next != __last) { if (__binary_pred(*__first, *__next)) { - _M_erase(__next); + __to_destroy.splice(__to_destroy.begin(), *this, __next); +#if !_GLIBCXX_USE_CXX11_ABI _GLIBCXX20_ONLY( __removed++ ); +#endif } else __first = __next; __next = __first; } + +#if !_GLIBCXX_USE_CXX11_ABI return _GLIBCXX20_ONLY( __removed ); +#else + return _GLIBCXX20_ONLY( __to_destroy.size() ); +#endif } #undef _GLIBCXX20_ONLY diff --git a/libstdc++-v3/include/debug/forward_list b/libstdc++-v3/include/debug/forward_list index fc6bf6359e9..7a00417ccb2 100644 --- a/libstdc++-v3/include/debug/forward_list +++ b/libstdc++-v3/include/debug/forward_list @@ -452,21 +452,15 @@ namespace __debug return { _Base::insert_after(__pos.base(), __il), this }; } - private: - _Base_iterator - _M_erase_after(_Base_const_iterator __pos) - { - _Base_const_iterator __next = std::next(__pos); - this->_M_invalidate_if([__next](_Base_const_iterator __it) - { return __it == __next; }); - return _Base::erase_after(__pos); - } - public: iterator erase_after(const_iterator __pos) { __glibcxx_check_erase_after(__pos); - return { _M_erase_after(__pos.base()), this }; + + _Base_const_iterator __next = std::next(__pos.base()); + this->_M_invalidate_if([__next](_Base_const_iterator __it) + { return __it == __next; }); + return { _Base::erase_after(__pos.base()), this }; } iterator @@ -691,29 +685,23 @@ namespace __debug return _Base::remove(__val); size_type __removed __attribute__((__unused__)) = 0; - _Base_iterator __x = _Base::before_begin(); - _Base_iterator __old = __x++; - _Base_iterator __extra = _Base::end(); - while (__x != _Base::end()) + _Base __to_destroy(get_allocator()); + _Base_const_iterator __x = _Base::cbefore_begin(); + _Base_const_iterator __old = __x++; + while (__x != _Base::cend()) { if (*__x == __val) { - if (std::__addressof(*__x) != std::__addressof(__val)) - { - __x = _M_erase_after(__old); - _GLIBCXX20_ONLY( __removed++ ); - continue; - } - else - __extra = __old; + _Base_const_iterator __next = std::next(__old); + this->_M_invalidate_if([__next](_Base_const_iterator __it) + { return __it == __next; }); + __to_destroy.splice_after(__to_destroy.cbefore_begin(), + _M_base(), __old); + __x = __old; + _GLIBCXX20_ONLY( __removed++ ); } - __old = __x++; - } - if (__extra != _Base::end()) - { - this->_M_erase_after(__extra); - _GLIBCXX20_ONLY( __removed++ ); + __old = __x++; } return _GLIBCXX20_ONLY( __removed ); @@ -727,16 +715,23 @@ namespace __debug return _Base::remove_if(__pred); size_type __removed __attribute__((__unused__)) = 0; + _Base __to_destroy(get_allocator()); _Base_iterator __x = _Base::before_begin(); _Base_iterator __old = __x++; while (__x != _Base::end()) - if (__pred(*__x)) - { - __x = _M_erase_after(__old); - _GLIBCXX20_ONLY( __removed++ ); - } - else + { + if (__pred(*__x)) + { + this->_M_invalidate_if([__x](_Base_const_iterator __it) + { return __it == __x; }); + __to_destroy.splice_after(__to_destroy.cbefore_begin(), + _M_base(), __old); + __x = __old; + _GLIBCXX20_ONLY( __removed++ ); + } + __old = __x++; + } return _GLIBCXX20_ONLY( __removed ); } @@ -753,22 +748,27 @@ namespace __debug if (!this->_M_iterators && !this->_M_const_iterators) return _Base::unique(__binary_pred); - _Base_iterator __first = _Base::begin(); - _Base_iterator __last = _Base::end(); + _Base_const_iterator __first = _Base::cbegin(); + _Base_const_iterator __last = _Base::cend(); if (__first == __last) return _GLIBCXX20_ONLY(0); size_type __removed __attribute__((__unused__)) = 0; - _Base_iterator __next = std::next(__first); + _Base __to_destroy(get_allocator()); + _Base_const_iterator __next = std::next(__first); while (__next != __last) { if (__binary_pred(*__first, *__next)) { - __next = _M_erase_after(__first); + this->_M_invalidate_if([__next](_Base_const_iterator __it) + { return __it == __next; }); + __to_destroy.splice_after(__to_destroy.cbefore_begin(), + _M_base(), __first); + __next = __first; _GLIBCXX20_ONLY( __removed++ ); } - else - __first = __next++; + + __first = __next++; } return _GLIBCXX20_ONLY( __removed ); diff --git a/libstdc++-v3/include/debug/list b/libstdc++-v3/include/debug/list index 8f2a8cb0f01..b5652fd9fdc 100644 --- a/libstdc++-v3/include/debug/list +++ b/libstdc++-v3/include/debug/list @@ -681,36 +681,36 @@ namespace __debug if (!this->_M_iterators && !this->_M_const_iterators) return _Base::remove(__value); +#if !_GLIBCXX_USE_CXX11_ABI size_type __removed __attribute__((__unused__)) = 0; +#endif + _Base __to_destroy(get_allocator()); _Base_iterator __first = _Base::begin(); _Base_iterator __last = _Base::end(); - _Base_iterator __extra = __last; while (__first != __last) { + _Base_iterator __next = __first; + ++__next; if (*__first == __value) - // _GLIBCXX_RESOLVE_LIB_DEFECTS - // 526. Is it undefined if a function in the standard changes - // in parameters? - if (std::__addressof(*__first) != std::__addressof(__value)) - { - __first = _M_erase(__first); - _GLIBCXX20_ONLY( __removed++ ); - } - else - { - __extra = __first; - ++__first; - } - else - ++__first; - } + { + // _GLIBCXX_RESOLVE_LIB_DEFECTS + // 526. Is it undefined if a function in the standard changes + // in parameters? + this->_M_invalidate_if(_Equal(__first)); + __to_destroy.splice(__to_destroy.begin(), _M_base(), __first); +#if !_GLIBCXX_USE_CXX11_ABI + _GLIBCXX20_ONLY( __removed++ ); +#endif + } - if (__extra != __last) - { - _M_erase(__extra); - _GLIBCXX20_ONLY( __removed++ ); + __first = __next; } + +#if !_GLIBCXX_USE_CXX11_ABI return _GLIBCXX20_ONLY( __removed ); +#else + return _GLIBCXX20_ONLY( __to_destroy.size() ); +#endif } template<class _Predicate> @@ -720,17 +720,31 @@ namespace __debug if (!this->_M_iterators && !this->_M_const_iterators) return _Base::remove_if(__pred); +#if !_GLIBCXX_USE_CXX11_ABI size_type __removed __attribute__((__unused__)) = 0; +#endif + _Base __to_destroy(get_allocator()); for (_Base_iterator __x = _Base::begin(); __x != _Base::end(); ) + { + _Base_iterator __next = __x; + ++__next; if (__pred(*__x)) { - __x = _M_erase(__x); + this->_M_invalidate_if(_Equal(__x)); + __to_destroy.splice(__to_destroy.begin(), _M_base(), __x); +#if !_GLIBCXX_USE_CXX11_ABI _GLIBCXX20_ONLY( __removed++ ); +#endif } - else - ++__x; + __x = __next; + } + +#if !_GLIBCXX_USE_CXX11_ABI return _GLIBCXX20_ONLY( __removed ); +#else + return _GLIBCXX20_ONLY( __to_destroy.size() ); +#endif } _GLIBCXX_LIST_REMOVE_RETURN_TYPE_TAG @@ -743,21 +757,31 @@ namespace __debug if (empty()) return _GLIBCXX20_ONLY(0); +#if !_GLIBCXX_USE_CXX11_ABI size_type __removed __attribute__((__unused__)) = 0; +#endif + _Base __to_destroy(get_allocator()); _Base_iterator __first = _Base::begin(); _Base_iterator __last = _Base::end(); _Base_iterator __next = __first; while (++__next != __last) if (*__first == *__next) { - _M_erase(__next); + this->_M_invalidate_if(_Equal(__next)); + __to_destroy.splice(__to_destroy.begin(), _M_base(), __next); __next = __first; +#if !_GLIBCXX_USE_CXX11_ABI _GLIBCXX20_ONLY( __removed++ ); +#endif } else __first = __next; +#if !_GLIBCXX_USE_CXX11_ABI return _GLIBCXX20_ONLY( __removed ); +#else + return _GLIBCXX20_ONLY( __to_destroy.size() ); +#endif } template<class _BinaryPredicate> @@ -770,21 +794,32 @@ namespace __debug if (empty()) return _GLIBCXX20_ONLY(0); + +#if !_GLIBCXX_USE_CXX11_ABI size_type __removed __attribute__((__unused__)) = 0; +#endif + _Base __to_destroy(get_allocator()); _Base_iterator __first = _Base::begin(); _Base_iterator __last = _Base::end(); - _Base_iterator __next = __first;; + _Base_iterator __next = __first; while (++__next != __last) if (__binary_pred(*__first, *__next)) { - _M_erase(__next); + this->_M_invalidate_if(_Equal(__next)); + __to_destroy.splice(__to_destroy.begin(), _M_base(), __next); __next = __first; +#if !_GLIBCXX_USE_CXX11_ABI _GLIBCXX20_ONLY( __removed++ ); +#endif } else __first = __next; - return _GLIBCXX20_ONLY( __removed ); +#if !_GLIBCXX_USE_CXX11_ABI + return _GLIBCXX20_ONLY( __removed ); +#else + return _GLIBCXX20_ONLY( __to_destroy.size() ); +#endif } #undef _GLIBCXX_LIST_REMOVE_RETURN_TYPE_TAG diff --git a/libstdc++-v3/testsuite/23_containers/forward_list/operations/91620.cc b/libstdc++-v3/testsuite/23_containers/forward_list/operations/91620.cc new file mode 100644 index 00000000000..a3127f6ee68 --- /dev/null +++ b/libstdc++-v3/testsuite/23_containers/forward_list/operations/91620.cc @@ -0,0 +1,88 @@ +// { dg-do run { target c++11 } } +// { dg-options "-g -O0" } + +// +// Copyright (C) 2020 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/>. + +#include <forward_list> +#include <memory> +#include <testsuite_hooks.h> + +struct PredLWG526 +{ + PredLWG526(int i) : i_(i) {}; + ~PredLWG526() { i_ = -32767; } + + bool + operator() (const PredLWG526& p) const { return p.i_ == i_; } + + bool + operator==(int i) const { return i == i_; } + + bool + operator() (const PredLWG526& lhs, const PredLWG526& rhs) const + { + VERIFY( i_ != -32767 ); + return lhs.i_ == rhs.i_; + } + + int i_; +}; + +void test01() +{ + int a1[] = {1, 2, 1, 3, 5, 8, 11}; + int a2[] = {2, 3, 5, 8, 11}; + std::forward_list<PredLWG526> fl(a1, a1 + 7); + + VERIFY( std::distance(fl.begin(), fl.end()) == 7 ); + + fl.remove_if(std::cref(fl.front())); + VERIFY( std::distance(fl.begin(), fl.end()) == 5 ); + for (size_t i = 0; !fl.empty(); ++i) + { + VERIFY( fl.front() == a2[i] ); + fl.pop_front(); + } +} + +void test02() +{ + int a1[] = {1, 1, 1, 2, 3, 5, 8, 11}; + int a2[] = {1, 2, 3, 5, 8, 11}; + std::forward_list<PredLWG526> fl(a1, a1 + 8); + + VERIFY( std::distance(fl.begin(), fl.end()) == 8 ); + + auto it = fl.begin(); + ++it; + fl.unique(std::cref(*it)); + VERIFY( std::distance(fl.begin(), fl.end()) == 6 ); + for (size_t i = 0; !fl.empty(); ++i) + { + VERIFY( fl.front() == a2[i] ); + fl.pop_front(); + } +} + +int main() +{ + test01(); + test02(); + return 0; +} diff --git a/libstdc++-v3/testsuite/23_containers/list/operations/91620.cc b/libstdc++-v3/testsuite/23_containers/list/operations/91620.cc new file mode 100644 index 00000000000..64c0998082d --- /dev/null +++ b/libstdc++-v3/testsuite/23_containers/list/operations/91620.cc @@ -0,0 +1,110 @@ +// { dg-do run { target c++11 } } +// { dg-options "-g -O0" } + +// +// Copyright (C) 2020 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/>. + +#include <list> +#include <memory> +#include <testsuite_hooks.h> + +struct PredLWG526 +{ + PredLWG526(int i) : i_(i) {}; + ~PredLWG526() { i_ = -32767; } + + bool + operator() (const PredLWG526& p) const { return p.i_ == i_; } + + bool + operator==(int i) const { return i == i_; } + + bool + operator()(const PredLWG526& lhs, const PredLWG526& rhs) const + { + VERIFY( i_ != -32767 ); + return lhs.i_ == rhs.i_; + } + + friend bool + operator==(const PredLWG526& lhs, const PredLWG526& rhs) + { return lhs.i_ == rhs.i_; } + + int i_; +}; + +void test01() +{ + int a1[] = {1, 2, 1, 3, 5, 8, 11}; + int a2[] = {2, 3, 5, 8, 11}; + std::list<PredLWG526> l(a1, a1 + 7); + + VERIFY( std::distance(l.begin(), l.end()) == 7 ); + + l.remove(l.front()); + VERIFY( std::distance(l.begin(), l.end()) == 5 ); + for (size_t i = 0; !l.empty(); ++i) + { + VERIFY( l.front() == a2[i] ); + l.pop_front(); + } +} + +void test02() +{ + int a1[] = {1, 2, 1, 3, 5, 8, 11}; + int a2[] = {2, 3, 5, 8, 11}; + std::list<PredLWG526> l(a1, a1 + 7); + + VERIFY( std::distance(l.begin(), l.end()) == 7 ); + + l.remove_if(std::cref(l.front())); + VERIFY( std::distance(l.begin(), l.end()) == 5 ); + for (size_t i = 0; !l.empty(); ++i) + { + VERIFY( l.front() == a2[i] ); + l.pop_front(); + } +} + +void test03() +{ + int a1[] = {1, 1, 1, 2, 3, 5, 8, 11}; + int a2[] = {1, 2, 3, 5, 8, 11}; + std::list<PredLWG526> l(a1, a1 + 8); + + VERIFY( std::distance(l.begin(), l.end()) == 8 ); + + auto it = l.begin(); + ++it; + l.unique(std::cref(*it)); + VERIFY( std::distance(l.begin(), l.end()) == 6 ); + for (size_t i = 0; !l.empty(); ++i) + { + VERIFY( l.front() == a2[i] ); + l.pop_front(); + } +} + +int main() +{ + test01(); + test02(); + test03(); + return 0; +} ^ permalink raw reply [flat|nested] 7+ messages in thread
* Re: [PATCH] PR libstdc++/91620 Implement DR 526 for std::[forward_]list::remove_if/unique 2019-12-27 15:54 [PATCH] PR libstdc++/91620 Implement DR 526 for std::[forward_]list::remove_if/unique François Dumont 2020-03-03 5:42 ` François Dumont 2020-08-10 19:07 ` François Dumont @ 2020-08-11 9:10 ` Jonathan Wakely 2020-08-11 9:12 ` Jonathan Wakely 2 siblings, 1 reply; 7+ messages in thread From: Jonathan Wakely @ 2020-08-11 9:10 UTC (permalink / raw) To: François Dumont; +Cc: libstdc++, gcc-patches On 27/12/19 11:57 +0100, François Dumont wrote: >Here is the patch to extend DR 526 to forward_list and list remove_if >and unique. > >As the adopted pattern is simpler I also applied it to the remove methods. > >   PR libstdc++/91620 >   * include/bits/forward_list.tcc (forward_list<>::remove): Collect nodes >   to destroy in an intermediate forward_list. >   (forward_list<>::remove_if, forward_list<>::unique): Likewise. >   * include/bits/list.tcc (list<>::remove, list<>::unique): Likewise. >   (list<>::remove_if): Likewise. >   * include/debug/forward_list (forward_list<>::_M_erase_after): Remove. >   (forward_list<>::erase_after): Adapt. >   (forward_list<>::remove, forward_list<>::remove_if): Collect nodes to >   destroy in an intermediate forward_list. >   (forward_list<>::unique): Likewise. >   * include/debug/list (list<>::remove, list<>::unique): Likewise. >   (list<>::remove_if): Likewise. > >Tested under Linux x86_64 normal and debug modes. > >Ok to commit ? > >François > >diff --git a/libstdc++-v3/include/bits/forward_list.tcc b/libstdc++-v3/include/bits/forward_list.tcc >index 088111e3330..70de7e75a43 100644 >--- a/libstdc++-v3/include/bits/forward_list.tcc >+++ b/libstdc++-v3/include/bits/forward_list.tcc >@@ -290,30 +290,19 @@ _GLIBCXX_BEGIN_NAMESPACE_CONTAINER > remove(const _Tp& __val) -> __remove_return_type > { > size_type __removed __attribute__((__unused__)) = 0; >- _Node_base* __curr = &this->_M_impl._M_head; >- _Node_base* __extra = nullptr; >+ forward_list __to_destroy(get_allocator()); > >- while (_Node* __tmp = static_cast<_Node*>(__curr->_M_next)) >- { >+ auto __prev_it = cbefore_begin(); >+ while (_Node* __tmp = static_cast<_Node*>(__prev_it._M_node->_M_next)) > if (*__tmp->_M_valptr() == __val) > { >- if (__tmp->_M_valptr() != std::__addressof(__val)) >- { >- this->_M_erase_after(__curr); >+ __to_destroy.splice_after(__to_destroy.cbefore_begin(), >+ *this, __prev_it); > _GLIBCXX20_ONLY( __removed++ ); >- continue; > } > else >- __extra = __curr; >- } >- __curr = __curr->_M_next; >- } >+ ++__prev_it; > >- if (__extra) >- { >- this->_M_erase_after(__extra); >- _GLIBCXX20_ONLY( __removed++ ); >- } > return _GLIBCXX20_ONLY( __removed ); > } > >@@ -324,17 +313,19 @@ _GLIBCXX_BEGIN_NAMESPACE_CONTAINER > remove_if(_Pred __pred) -> __remove_return_type > { > size_type __removed __attribute__((__unused__)) = 0; >- _Node_base* __curr = &this->_M_impl._M_head; >- while (_Node* __tmp = static_cast<_Node*>(__curr->_M_next)) >- { >+ forward_list __to_destroy(get_allocator()); >+ >+ auto __prev_it = cbefore_begin(); >+ while (_Node* __tmp = static_cast<_Node*>(__prev_it._M_node->_M_next)) > if (__pred(*__tmp->_M_valptr())) > { >- this->_M_erase_after(__curr); >+ __to_destroy.splice_after(__to_destroy.cbefore_begin(), >+ *this, __prev_it); > _GLIBCXX20_ONLY( __removed++ ); > } > else >- __curr = __curr->_M_next; >- } >+ ++__prev_it; >+ > return _GLIBCXX20_ONLY( __removed ); > } > >@@ -348,19 +339,23 @@ _GLIBCXX_BEGIN_NAMESPACE_CONTAINER > iterator __last = end(); > if (__first == __last) > return _GLIBCXX20_ONLY(0); >+ >+ forward_list __to_destroy(get_allocator()); > size_type __removed __attribute__((__unused__)) = 0; > iterator __next = __first; > while (++__next != __last) > { > if (__binary_pred(*__first, *__next)) > { >- erase_after(__first); >+ __to_destroy.splice_after(__to_destroy.cbefore_begin(), >+ *this, __first); > _GLIBCXX20_ONLY( __removed++ ); > } > else > __first = __next; > __next = __first; > } >+ > return _GLIBCXX20_ONLY( __removed ); > } > >diff --git a/libstdc++-v3/include/bits/list.tcc b/libstdc++-v3/include/bits/list.tcc >index 0ac6654b079..5a6fd5b0824 100644 >--- a/libstdc++-v3/include/bits/list.tcc >+++ b/libstdc++-v3/include/bits/list.tcc >@@ -331,10 +331,12 @@ _GLIBCXX_BEGIN_NAMESPACE_CONTAINER > list<_Tp, _Alloc>:: > remove(const value_type& __value) > { >+#if !_GLIBCXX_USE_CXX11_ABI > size_type __removed __attribute__((__unused__)) = 0; >+#endif >+ list __to_destroy(get_allocator()); > iterator __first = begin(); > iterator __last = end(); >- iterator __extra = __last; > while (__first != __last) > { > iterator __next = __first; >@@ -344,22 +346,20 @@ _GLIBCXX_BEGIN_NAMESPACE_CONTAINER > // _GLIBCXX_RESOLVE_LIB_DEFECTS > // 526. Is it undefined if a function in the standard changes > // in parameters? >- if (std::__addressof(*__first) != std::__addressof(__value)) >- { >- _M_erase(__first); >+ __to_destroy.splice(__to_destroy.begin(), *this, __first); >+#if !_GLIBCXX_USE_CXX11_ABI > _GLIBCXX20_ONLY( __removed++ ); >+#endif The point of the _GLIBCXX20_ONLY macro was to avoid cluttering this function with #ifdef directives. If it's going to be full of #ifdef now anyway, there's no real benefit to using _GLIBCXX20_ONLY. This could now be: #if __cplusplus > 201703L && ! _GLIBCXX_USE_CXX11_ABI __removed++; #endif and so on. > } >- else >- __extra = __first; >- } >+ > __first = __next; > } >- if (__extra != __last) >- { >- _M_erase(__extra); >- _GLIBCXX20_ONLY( __removed++ ); >- } >+ >+#if !_GLIBCXX_USE_CXX11_ABI > return _GLIBCXX20_ONLY( __removed ); >+#else >+ return _GLIBCXX20_ONLY( __to_destroy.size() ); >+#endif Although this becomes pretty ugly: #if __cplusplus > 201703L # if _GLIBCXX_USE_CXX11_ABI return __to_destroy.size(); # else return __removed; # endif #endif So maybe _GLIBCXX20_ONLY is still worthwhile. The other alternative would be to define a new type right at the start which keeps count if needed: #if __cplusplus > 201703L && ! _GLIBCXX_USE_CXX11_ABI struct _VictimList : list { void splice(list::iterator __p, list& __x, list::iterator __i) { list::splice(__p, __x, __i); ++_M_size; } size_type size() const { return _M_size; } size_type _M_size = 0; } __to_destroy; #else list __to_destroy; #endif // ... return _GLIBCXX20_ONLY(__to_destroy.size()); With this the only conditional compilation is at the start of the function and the one use of _GLIBCXX20_ONLY at the end. The rest of the function body has no #if or macros at all. Your patch is OK for master. We can revisit it later to tidy it up. Thanks. ^ permalink raw reply [flat|nested] 7+ messages in thread
* Re: [PATCH] PR libstdc++/91620 Implement DR 526 for std::[forward_]list::remove_if/unique 2020-08-11 9:10 ` Jonathan Wakely @ 2020-08-11 9:12 ` Jonathan Wakely 0 siblings, 0 replies; 7+ messages in thread From: Jonathan Wakely @ 2020-08-11 9:12 UTC (permalink / raw) To: François Dumont; +Cc: libstdc++, gcc-patches On 11/08/20 10:11 +0100, Jonathan Wakely wrote: >On 27/12/19 11:57 +0100, François Dumont wrote: >>Here is the patch to extend DR 526 to forward_list and list >>remove_if and unique. >> >>As the adopted pattern is simpler I also applied it to the remove methods. >> >>   PR libstdc++/91620 >>   * include/bits/forward_list.tcc (forward_list<>::remove): Collect nodes >>   to destroy in an intermediate forward_list. >>   (forward_list<>::remove_if, forward_list<>::unique): Likewise. >>   * include/bits/list.tcc (list<>::remove, list<>::unique): Likewise. >>   (list<>::remove_if): Likewise. >>   * include/debug/forward_list (forward_list<>::_M_erase_after): Remove. >>   (forward_list<>::erase_after): Adapt. >>   (forward_list<>::remove, forward_list<>::remove_if): Collect nodes to >>   destroy in an intermediate forward_list. >>   (forward_list<>::unique): Likewise. >>   * include/debug/list (list<>::remove, list<>::unique): Likewise. >>   (list<>::remove_if): Likewise. >> >>Tested under Linux x86_64 normal and debug modes. >> >>Ok to commit ? >> >>François >> > > >>diff --git a/libstdc++-v3/include/bits/forward_list.tcc b/libstdc++-v3/include/bits/forward_list.tcc >>index 088111e3330..70de7e75a43 100644 >>--- a/libstdc++-v3/include/bits/forward_list.tcc >>+++ b/libstdc++-v3/include/bits/forward_list.tcc >>@@ -290,30 +290,19 @@ _GLIBCXX_BEGIN_NAMESPACE_CONTAINER >> remove(const _Tp& __val) -> __remove_return_type >> { >> size_type __removed __attribute__((__unused__)) = 0; >>- _Node_base* __curr = &this->_M_impl._M_head; >>- _Node_base* __extra = nullptr; >>+ forward_list __to_destroy(get_allocator()); >> >>- while (_Node* __tmp = static_cast<_Node*>(__curr->_M_next)) >>- { >>+ auto __prev_it = cbefore_begin(); >>+ while (_Node* __tmp = static_cast<_Node*>(__prev_it._M_node->_M_next)) >> if (*__tmp->_M_valptr() == __val) >> { >>- if (__tmp->_M_valptr() != std::__addressof(__val)) >>- { >>- this->_M_erase_after(__curr); >>+ __to_destroy.splice_after(__to_destroy.cbefore_begin(), >>+ *this, __prev_it); >> _GLIBCXX20_ONLY( __removed++ ); >>- continue; >> } >> else >>- __extra = __curr; >>- } >>- __curr = __curr->_M_next; >>- } >>+ ++__prev_it; >> >>- if (__extra) >>- { >>- this->_M_erase_after(__extra); >>- _GLIBCXX20_ONLY( __removed++ ); >>- } >> return _GLIBCXX20_ONLY( __removed ); >> } >> >>@@ -324,17 +313,19 @@ _GLIBCXX_BEGIN_NAMESPACE_CONTAINER >> remove_if(_Pred __pred) -> __remove_return_type >> { >> size_type __removed __attribute__((__unused__)) = 0; >>- _Node_base* __curr = &this->_M_impl._M_head; >>- while (_Node* __tmp = static_cast<_Node*>(__curr->_M_next)) >>- { >>+ forward_list __to_destroy(get_allocator()); >>+ >>+ auto __prev_it = cbefore_begin(); >>+ while (_Node* __tmp = static_cast<_Node*>(__prev_it._M_node->_M_next)) >> if (__pred(*__tmp->_M_valptr())) >> { >>- this->_M_erase_after(__curr); >>+ __to_destroy.splice_after(__to_destroy.cbefore_begin(), >>+ *this, __prev_it); >> _GLIBCXX20_ONLY( __removed++ ); >> } >> else >>- __curr = __curr->_M_next; >>- } >>+ ++__prev_it; >>+ >> return _GLIBCXX20_ONLY( __removed ); >> } >> >>@@ -348,19 +339,23 @@ _GLIBCXX_BEGIN_NAMESPACE_CONTAINER >> iterator __last = end(); >> if (__first == __last) >> return _GLIBCXX20_ONLY(0); >>+ >>+ forward_list __to_destroy(get_allocator()); >> size_type __removed __attribute__((__unused__)) = 0; >> iterator __next = __first; >> while (++__next != __last) >> { >> if (__binary_pred(*__first, *__next)) >> { >>- erase_after(__first); >>+ __to_destroy.splice_after(__to_destroy.cbefore_begin(), >>+ *this, __first); >> _GLIBCXX20_ONLY( __removed++ ); >> } >> else >> __first = __next; >> __next = __first; >> } >>+ >> return _GLIBCXX20_ONLY( __removed ); >> } >> >>diff --git a/libstdc++-v3/include/bits/list.tcc b/libstdc++-v3/include/bits/list.tcc >>index 0ac6654b079..5a6fd5b0824 100644 >>--- a/libstdc++-v3/include/bits/list.tcc >>+++ b/libstdc++-v3/include/bits/list.tcc >>@@ -331,10 +331,12 @@ _GLIBCXX_BEGIN_NAMESPACE_CONTAINER >> list<_Tp, _Alloc>:: >> remove(const value_type& __value) >> { >>+#if !_GLIBCXX_USE_CXX11_ABI >> size_type __removed __attribute__((__unused__)) = 0; >>+#endif >>+ list __to_destroy(get_allocator()); >> iterator __first = begin(); >> iterator __last = end(); >>- iterator __extra = __last; >> while (__first != __last) >> { >> iterator __next = __first; >>@@ -344,22 +346,20 @@ _GLIBCXX_BEGIN_NAMESPACE_CONTAINER >> // _GLIBCXX_RESOLVE_LIB_DEFECTS >> // 526. Is it undefined if a function in the standard changes >> // in parameters? >>- if (std::__addressof(*__first) != std::__addressof(__value)) >>- { >>- _M_erase(__first); >>+ __to_destroy.splice(__to_destroy.begin(), *this, __first); >>+#if !_GLIBCXX_USE_CXX11_ABI >> _GLIBCXX20_ONLY( __removed++ ); >>+#endif > >The point of the _GLIBCXX20_ONLY macro was to avoid cluttering this >function with #ifdef directives. If it's going to be full of #ifdef >now anyway, there's no real benefit to using _GLIBCXX20_ONLY. > >This could now be: > >#if __cplusplus > 201703L && ! _GLIBCXX_USE_CXX11_ABI > __removed++; >#endif > >and so on. > >> } >>- else >>- __extra = __first; >>- } >>+ >> __first = __next; >> } >>- if (__extra != __last) >>- { >>- _M_erase(__extra); >>- _GLIBCXX20_ONLY( __removed++ ); >>- } >>+ >>+#if !_GLIBCXX_USE_CXX11_ABI >> return _GLIBCXX20_ONLY( __removed ); >>+#else >>+ return _GLIBCXX20_ONLY( __to_destroy.size() ); >>+#endif > >Although this becomes pretty ugly: > >#if __cplusplus > 201703L ># if _GLIBCXX_USE_CXX11_ABI > return __to_destroy.size(); ># else > return __removed; ># endif >#endif > >So maybe _GLIBCXX20_ONLY is still worthwhile. > >The other alternative would be to define a new type right at the start >which keeps count if needed: > >#if __cplusplus > 201703L && ! _GLIBCXX_USE_CXX11_ABI > struct _VictimList : list > { > void splice(list::iterator __p, list& __x, list::iterator __i) > { > list::splice(__p, __x, __i); > ++_M_size; > } > size_type size() const { return _M_size; } > size_type _M_size = 0; > } __to_destroy; >#else > list __to_destroy; >#endif > > // ... > > return _GLIBCXX20_ONLY(__to_destroy.size()); > >With this the only conditional compilation is at the start of the >function and the one use of _GLIBCXX20_ONLY at the end. The rest of >the function body has no #if or macros at all. > >Your patch is OK for master. We can revisit it later to tidy it up. Oops, I meant to reply to the patch with tests of course, not the original one. Please do commit the tests. ^ permalink raw reply [flat|nested] 7+ messages in thread
end of thread, other threads:[~2020-08-11 9:12 UTC | newest] Thread overview: 7+ messages (download: mbox.gz / follow: Atom feed) -- links below jump to the message on this page -- 2019-12-27 15:54 [PATCH] PR libstdc++/91620 Implement DR 526 for std::[forward_]list::remove_if/unique François Dumont 2020-03-03 5:42 ` François Dumont 2020-03-03 21:33 ` Jonathan Wakely 2020-03-03 21:37 ` Jonathan Wakely 2020-08-10 19:07 ` François Dumont 2020-08-11 9:10 ` Jonathan Wakely 2020-08-11 9:12 ` 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).