public inbox for libstdc++@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: Make tests less istreambuf_iterator implementation dependent
Date: Tue, 03 Oct 2017 14:20:00 -0000	[thread overview]
Message-ID: <20171003142023.GW4582@redhat.com> (raw)
In-Reply-To: <2285784f-dfce-3e48-30bf-902fe1d157b8@gmail.com>

On 02/10/17 07:43 +0200, François Dumont wrote:
>On 28/09/2017 23:56, Jonathan Wakely wrote:
>>On 28/09/17 21:59 +0200, François Dumont wrote:
>>>
>>>The current istreambuf_iterator implementation capture the current 
>>>streambuf state each time it is tested for eof or evaluated. This 
>>>is why I considered those tests as fragile.
>>
>>Yes, and I think that's non-conforming.
>>
>
>Good, then we have something to fix.
>
>>As I said in the other thread, I'd really like to see references to
>>the standard used to justify any changes to our istreambuf_iterator
>
>I think _M_c has been introduced to cover this paragraph of the Standard:
>
>24.6.3.1
>
>"1. Class istreambuf_iterator<charT,traits>::proxy is for exposition 
>only. An implementation is permit-
>ted to provide equivalent functionality without providing a class with 
>this name. Class istreambuf_-
>iterator<charT, traits>::proxy provides a temporary placeholder as the 
>return value of the post-
>increment operator (operator++). It keeps the character pointed to by 
>the previous value of the iterator for
>some possible future access to get the character."
>
>This is why it is being set in operator++(int):
>
>    istreambuf_iterator __old = *this;
>    __old._M_c = _M_sbuf->sbumpc();
>
>It is also the reason why libstdc++ fails:
>http://llvm.org/svn/llvm-project/libcxx/trunk/test/std/iterators/stream.iterators/istreambuf.iterator/istreambuf.iterator.cons/proxy.pass.cpp
>
>
>It looks like this test is simply not Standard conformant.

I think you're right, because it relies on a property that may not be
true:

  any copies of the previous
  value of r are no longer
  required either to be
  dereferenceable or to be in
  the domain of ==.

>I guess at some point _M_c started to be used also to cache the 
>streambuf::sgetc resulting in current situation.

And arguably that is not "equivalent functionality" to returning a
proxy object. Although the standard seems a bit underspecified.

>In attached patch I limit usage of _M_c to cover 24.6.3.1.1 point and 
>so made additional changes to 2.cc test case to demonstrate it.

Doesn't the modified test PASS anyway, even without changing how _M_c
is used?

Generally this looks good (I'm not sure if it addresses Petr's
concerns, but I don't think his "stop and go" usage is valid anyway -
it's certainly not portable or guaranteed by the standard).

>@@ -103,7 +103,7 @@ _GLIBCXX_BEGIN_NAMESPACE_VERSION
>       : _M_sbuf(0), _M_c(traits_type::eof()) { }
> 
> #if __cplusplus >= 201103L
>-      istreambuf_iterator(const istreambuf_iterator&) noexcept = default;
>+      istreambuf_iterator(const istreambuf_iterator&) = default;

Please don't remove this.

It's present in the standard, and it ensures we don't accidentally add
a member that makes the default noexcept(false).

> 
>       ~istreambuf_iterator() = default;
> #endif
>@@ -122,28 +122,29 @@ _GLIBCXX_BEGIN_NAMESPACE_VERSION
>       char_type
>       operator*() const
>       {
>+	int_type __c = _M_get();
>+
> #ifdef _GLIBCXX_DEBUG_PEDANTIC
> 	// Dereferencing a past-the-end istreambuf_iterator is a
> 	// libstdc++ extension
>-	__glibcxx_requires_cond(!_M_at_eof(),
>+	__glibcxx_requires_cond(!_S_at_eof(__c),
> 				_M_message(__gnu_debug::__msg_deref_istreambuf)
> 				._M_iterator(*this));
> #endif
>-	return traits_type::to_char_type(_M_get());
>+	return traits_type::to_char_type(__c);
>       }
> 
>       /// Advance the iterator.  Calls streambuf.sbumpc().
>       istreambuf_iterator&
>       operator++()
>       {
>-	__glibcxx_requires_cond(!_M_at_eof(),
>+	__glibcxx_requires_cond(_M_sbuf &&
>+				(!_S_at_eof(_M_c) || !_S_at_eof(_M_sbuf->sgetc())),
> 				_M_message(__gnu_debug::__msg_inc_istreambuf)
> 				._M_iterator(*this));
>-	if (_M_sbuf)
>-	  {

This check should be unnecessary, because it's undefined to increment
and end-of-stream iterator, but I wonder if anyone is relying on it
being currently a no-op, rather than crashing.

Let's remove the check, and if it causes too many problems we can
restore it as:

  if (__builtin_expect(_M_sbuf != 0, 1))

>+
> 	_M_sbuf->sbumpc();
> 	_M_c = traits_type::eof();
>-	  }
> 	return *this;
>       }
> 
>@@ -151,16 +152,14 @@ _GLIBCXX_BEGIN_NAMESPACE_VERSION
>       istreambuf_iterator
>       operator++(int)
>       {
>-	__glibcxx_requires_cond(!_M_at_eof(),
>+	__glibcxx_requires_cond(_M_sbuf &&
>+				(!_S_at_eof(_M_c) || !_S_at_eof(_M_sbuf->sgetc())),
> 				_M_message(__gnu_debug::__msg_inc_istreambuf)
> 				._M_iterator(*this));
> 
> 	istreambuf_iterator __old = *this;
>-	if (_M_sbuf)
>-	  {
> 	__old._M_c = _M_sbuf->sbumpc();
> 	_M_c = traits_type::eof();
>-	  }
> 	return __old;
>       }
> 
>@@ -176,26 +175,21 @@ _GLIBCXX_BEGIN_NAMESPACE_VERSION
>       int_type
>       _M_get() const
>       {
>-	const int_type __eof = traits_type::eof();
>-	int_type __ret = __eof;
>-	if (_M_sbuf)
>-	  {
>-	    if (!traits_type::eq_int_type(_M_c, __eof))
>-	      __ret = _M_c;
>-	    else if (!traits_type::eq_int_type((__ret = _M_sbuf->sgetc()),
>-					       __eof))
>-	      _M_c = __ret;

Removing this assignment means that we will call _M_sbuf->sgetc() for
every dereference operation, instead of caching the current value in
_M_c. I think that's probably OK, because it's unlikely that
performance critical code is dereferencing the same iterator
repeatedly without incrementing it.

And getting rid of the mutable member is a Good Thing.

An alternative would be to cache it in operator*() instead of in
_M_get(), which would still give the desired change of not updating it
when _M_get() is used elsewhere. But would still involve setting a
mutable member in a const function.

>-	    else
>+	int_type __ret = _M_c;
>+	if (_M_sbuf && _S_at_eof(__ret) && _S_at_eof(__ret = _M_sbuf->sgetc()))
> 	  _M_sbuf = 0;

It's unfortunate that we still have this assignment to a mutable
member in a const function, but I think it's necessary to avoid an
end-of-stream iterator becoming valid again.

>-	  }
> 	return __ret;
>       }
> 
>       bool
>       _M_at_eof() const
>+      { return _S_at_eof(_M_get()); }
>+
>+      static bool
>+      _S_at_eof(int_type __c)

This should be called _S_is_eof, since it tests if the argument **is**
the EOF character. It doesn't test if the stream is **at** EOF, like
_M_at_eof does.

So with a suitable ChangeLog, the "noexcept" kept on the constructor,
and renaming _S_at_eof to _S_is_eof, this is OK for trunk.



  reply	other threads:[~2017-10-03 14:20 UTC|newest]

Thread overview: 61+ messages / expand[flat|nested]  mbox.gz  Atom feed  top
2017-09-27 20:16 François Dumont
2017-09-28 12:12 ` Jonathan Wakely
2017-09-28 19:59   ` François Dumont
2017-09-28 21:56     ` Jonathan Wakely
2017-10-02  5:43       ` François Dumont
2017-10-03 14:20         ` Jonathan Wakely [this message]
2017-10-04 16:21           ` François Dumont
2017-10-04 23:23             ` Jonathan Wakely
2017-11-15 20:52             ` [PATCH 1/4] Revert "2017-10-04 Petr Ovtchenkov <ptr@void-ptr.info>" Petr Ovtchenkov
2017-11-15 20:52               ` [PATCH 2/4] libstdc++: istreambuf_iterator keep attached streambuf Petr Ovtchenkov
2017-11-15 20:52                 ` [PATCH 3/4] libstdc++: avoid character accumulation in istreambuf_iterator Petr Ovtchenkov
2017-11-15 20:52                   ` [PATCH 4/4] libstdc++: immutable _M_sbuf " Petr Ovtchenkov
2017-11-15 21:31                   ` [PATCH 3/4] libstdc++: avoid character accumulation " Paolo Carlini
2017-11-16  5:32                     ` Petr Ovtchenkov
2017-11-16  9:39                       ` Paolo Carlini
2017-11-16 11:03                         ` Petr Ovtchenkov
2017-11-16 11:29                           ` Paolo Carlini
2017-11-16 11:41                             ` Petr Ovtchenkov
2017-11-16 11:51                               ` Paolo Carlini
2017-11-16 10:56               ` [PATCH 1/4] Revert "2017-10-04 Petr Ovtchenkov <ptr@void-ptr.info>" Jonathan Wakely
2017-11-16 11:35                 ` Petr Ovtchenkov
2017-11-16 11:39                   ` Jonathan Wakely
2017-11-16 11:40                     ` Jonathan Wakely
2017-11-16 11:57                     ` Petr Ovtchenkov
  -- strict thread matches above, loose matches on Subject: below --
2017-10-13 17:14 Make istreambuf_iterator::_M_sbuf immutable and add debug checks François Dumont
2017-10-23 19:08 ` François Dumont
2017-11-06 21:19   ` François Dumont
2017-11-16  5:52     ` Petr Ovtchenkov
2017-11-16 10:57       ` Jonathan Wakely
2017-11-16 11:46         ` Jonathan Wakely
2017-11-16 12:08           ` Petr Ovtchenkov
2017-11-16 17:40           ` François Dumont
2017-11-16 18:12             ` Petr Ovtchenkov
2017-11-16 21:31               ` François Dumont
2017-09-23  7:10 [PATCH] libstdc++: istreambuf_iterator keep attached streambuf Petr Ovtchenkov
2017-09-25 13:46 ` Jonathan Wakely
2017-09-28 10:34 ` Jonathan Wakely
2017-09-28 12:06   ` Petr Ovtchenkov
2017-09-28 12:38     ` Jonathan Wakely
2017-10-03 20:39       ` Petr Ovtchenkov
2017-10-04  5:04         ` [PATCH v2] " Petr Ovtchenkov
2017-10-06 16:01         ` [PATCH] libstdc++: istreambuf_iterator proxy (was: keep attached streambuf) François Dumont
2017-10-06 18:00           ` Petr Ovtchenkov
2017-10-08 14:59             ` [PATCH] libstdc++: istreambuf_iterator proxy François Dumont
2017-10-09 19:32               ` Petr Ovtchenkov
2017-10-10  5:52               ` Petr Ovtchenkov
2017-10-10 14:21           ` [PATCH] libstdc++: istreambuf_iterator proxy (was: keep attached streambuf) Jonathan Wakely
2017-08-24 11:27 [PATCH] streambuf_iterator: avoid debug-dependent behaviour Petr Ovtchenkov
2017-08-29 20:02 ` François Dumont
2017-08-30  5:05   ` Petr Ovtchenkov
2017-08-31 20:30     ` François Dumont
2017-09-01  9:10 ` Jonathan Wakely
2017-09-07 21:02   ` François Dumont
2017-09-08  5:47     ` Petr Ovtchenkov
2017-09-08  6:15       ` François Dumont
2017-09-09 20:17       ` François Dumont
2017-09-21  5:46         ` François Dumont
2017-09-28 10:50           ` Jonathan Wakely
2017-09-28 10:58             ` Jonathan Wakely
2017-09-21 18:23   ` Petr Ovtchenkov
2017-09-25  9:34     ` Petr Ovtchenkov

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=20171003142023.GW4582@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).