public inbox for gcc-patches@gcc.gnu.org
 help / color / mirror / Atom feed
From: Jonathan Wakely <jwakely@redhat.com>
To: "François Dumont" <frs.dumont@gmail.com>
Cc: "libstdc++@gcc.gnu.org" <libstdc++@gcc.gnu.org>,
	gcc-patches <gcc-patches@gcc.gnu.org>
Subject: Re: [PATCH] PR libstdc++/91620 Implement DR 526 for std::[forward_]list::remove_if/unique
Date: Tue, 11 Aug 2020 10:10:58 +0100	[thread overview]
Message-ID: <20200811091058.GZ3400@redhat.com> (raw)
In-Reply-To: <24962b65-4e23-2a3b-ea50-2eef3e9c98a8@gmail.com>

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.



  parent reply	other threads:[~2020-08-11  9:11 UTC|newest]

Thread overview: 7+ messages / expand[flat|nested]  mbox.gz  Atom feed  top
2019-12-27 15:54 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 [this message]
2020-08-11  9:12   ` Jonathan Wakely

Reply instructions:

You may reply publicly to this message via plain-text email
using any one of the following methods:

* Save the following mbox file, import it into your mail client,
  and reply-to-all from there: mbox

  Avoid top-posting and favor interleaved quoting:
  https://en.wikipedia.org/wiki/Posting_style#Interleaved_style

* Reply using the --to, --cc, and --in-reply-to
  switches of git-send-email(1):

  git send-email \
    --in-reply-to=20200811091058.GZ3400@redhat.com \
    --to=jwakely@redhat.com \
    --cc=frs.dumont@gmail.com \
    --cc=gcc-patches@gcc.gnu.org \
    --cc=libstdc++@gcc.gnu.org \
    /path/to/YOUR_REPLY

  https://kernel.org/pub/software/scm/git/docs/git-send-email.html

* If your mail client supports setting the In-Reply-To header
  via mailto: links, try the mailto: link
Be sure your reply has a Subject: header at the top and a blank line before the message body.
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).