From 638ad333ec544ed7873ed6e13b6f095d6d58fd3b Mon Sep 17 00:00:00 2001 From: Jonathan Wakely Date: Wed, 19 Jun 2019 23:57:10 +0100 Subject: [PATCH] Fix non-standard behaviour of std::istream_iterator The current implementation of istream_iterator allows the iterator to be reused after reaching end-of-stream, so that subsequent reads from the stream can succeed (e.g. if the stream state has been cleared and stream position changed from EOF). The P0738R2 paper clarified that the expected behaviour is to set the stream pointer to null after reaching end-of-stream, preventing further reads. This implements that requirement, and adds the new default constructor to std::ostream_iterator. * include/bits/stream_iterator.h (istream_iterator::_M_equal()): Make private. (istream_iterator::_M_read()): Do not check stream state before attempting extraction. Set stream pointer to null when extraction fails (P0738R2). (operator==(const istream_iterator&, const istream_iterator&)): Change to be a hidden friend of istream_iterator. (operator!=(const istream_iterator&, const istream_iterator&)): Likewise. (ostream_iterator::ostream_iterator()): Add default constructor. (ostream_iterator::ostream_iterator(ostream_type*, const C*)): Use addressof. * testsuite/24_iterators/istream_iterator/1.cc: New test. * testsuite/24_iterators/ostream_iterator/1.cc: New test. * testsuite/24_iterators/ostream_iterator/70766.cc: Also check constructor taking a string. * testsuite/24_iterators/ostream_iterator/requirements/constexpr.cc: New test. From-SVN: r272491 --- libstdc++-v3/ChangeLog | 21 ++++++ libstdc++-v3/include/bits/stream_iterator.h | 61 ++++++++++------- .../testsuite/24_iterators/istream_iterator/1.cc | 77 ++++++++++++++++++++++ .../testsuite/24_iterators/ostream_iterator/1.cc | 48 ++++++++++++++ .../24_iterators/ostream_iterator/70766.cc | 1 + .../ostream_iterator/requirements/constexpr.cc | 24 +++++++ 6 files changed, 207 insertions(+), 25 deletions(-) create mode 100644 libstdc++-v3/testsuite/24_iterators/istream_iterator/1.cc create mode 100644 libstdc++-v3/testsuite/24_iterators/ostream_iterator/1.cc create mode 100644 libstdc++-v3/testsuite/24_iterators/ostream_iterator/requirements/constexpr.cc diff --git a/libstdc++-v3/ChangeLog b/libstdc++-v3/ChangeLog index a481420..40df69e 100644 --- a/libstdc++-v3/ChangeLog +++ b/libstdc++-v3/ChangeLog @@ -1,3 +1,24 @@ +2019-06-19 Jonathan Wakely + + * include/bits/stream_iterator.h (istream_iterator::_M_equal()): Make + private. + (istream_iterator::_M_read()): Do not check stream state before + attempting extraction. Set stream pointer to null when extraction + fails (P0738R2). + (operator==(const istream_iterator&, const istream_iterator&)): Change + to be a hidden friend of istream_iterator. + (operator!=(const istream_iterator&, const istream_iterator&)): + Likewise. + (ostream_iterator::ostream_iterator()): Add default constructor. + (ostream_iterator::ostream_iterator(ostream_type*, const C*)): Use + addressof. + * testsuite/24_iterators/istream_iterator/1.cc: New test. + * testsuite/24_iterators/ostream_iterator/1.cc: New test. + * testsuite/24_iterators/ostream_iterator/70766.cc: Also check + constructor taking a string. + * testsuite/24_iterators/ostream_iterator/requirements/constexpr.cc: + New test. + 2019-06-19 Michael Weghorn Jonathan Wakely diff --git a/libstdc++-v3/include/bits/stream_iterator.h b/libstdc++-v3/include/bits/stream_iterator.h index bc3353a..247ad53 100644 --- a/libstdc++-v3/include/bits/stream_iterator.h +++ b/libstdc++-v3/include/bits/stream_iterator.h @@ -57,6 +57,9 @@ _GLIBCXX_BEGIN_NAMESPACE_VERSION private: istream_type* _M_stream; _Tp _M_value; + // This bool becomes false at end-of-stream. It should be sufficient to + // check _M_stream != nullptr instead, but historically we did not set + // _M_stream to null when reaching the end, so we need to keep this flag. bool _M_ok; public: @@ -66,7 +69,7 @@ _GLIBCXX_BEGIN_NAMESPACE_VERSION /// Construct start of input stream iterator. istream_iterator(istream_type& __s) - : _M_stream(std::__addressof(__s)) + : _M_stream(std::__addressof(__s)), _M_ok(true) { _M_read(); } istream_iterator(const istream_iterator& __obj) @@ -76,6 +79,7 @@ _GLIBCXX_BEGIN_NAMESPACE_VERSION #if __cplusplus >= 201103L istream_iterator& operator=(const istream_iterator&) = default; + ~istream_iterator() = default; #endif const _Tp& @@ -111,36 +115,37 @@ _GLIBCXX_BEGIN_NAMESPACE_VERSION return __tmp; } + private: bool _M_equal(const istream_iterator& __x) const - { return (_M_ok == __x._M_ok) && (!_M_ok || _M_stream == __x._M_stream); } + { + // Ideally this would just return _M_stream == __x._M_stream, + // but code compiled with old versions never sets _M_stream to null. + return (_M_ok == __x._M_ok) && (!_M_ok || _M_stream == __x._M_stream); + } - private: void _M_read() { - _M_ok = (_M_stream && *_M_stream) ? true : false; - if (_M_ok) - { - *_M_stream >> _M_value; - _M_ok = *_M_stream ? true : false; - } + if (_M_stream && !(*_M_stream >> _M_value)) + { + _M_stream = 0; + _M_ok = false; + } } - }; - /// Return true if x and y are both end or not end, or x and y are the same. - template - inline bool - operator==(const istream_iterator<_Tp, _CharT, _Traits, _Dist>& __x, - const istream_iterator<_Tp, _CharT, _Traits, _Dist>& __y) - { return __x._M_equal(__y); } - - /// Return false if x and y are both end or not end, or x and y are the same. - template - inline bool - operator!=(const istream_iterator<_Tp, _CharT, _Traits, _Dist>& __x, - const istream_iterator<_Tp, _CharT, _Traits, _Dist>& __y) - { return !__x._M_equal(__y); } + /// Return true if the iterators refer to the same stream, + /// or are both at end-of-stream. + friend bool + operator==(const istream_iterator& __x, const istream_iterator& __y) + { return __x._M_equal(__y); } + + /// Return true if the iterators refer to different streams, + /// or if one is at end-of-stream and the other is not. + friend bool + operator!=(const istream_iterator& __x, const istream_iterator& __y) + { return !__x._M_equal(__y); } + }; /** * @brief Provides output iterator semantics for streams. @@ -171,6 +176,11 @@ _GLIBCXX_BEGIN_NAMESPACE_VERSION const _CharT* _M_string; public: +#if __cplusplus > 201703L + constexpr ostream_iterator() noexcept + : _M_stream(nullptr), _M_string(nullptr) { } +#endif + /// Construct from an ostream. ostream_iterator(ostream_type& __s) : _M_stream(std::__addressof(__s)), _M_string(0) {} @@ -186,7 +196,7 @@ _GLIBCXX_BEGIN_NAMESPACE_VERSION * @param __c CharT delimiter string to insert. */ ostream_iterator(ostream_type& __s, const _CharT* __c) - : _M_stream(&__s), _M_string(__c) { } + : _M_stream(std::__addressof(__s)), _M_string(__c) { } /// Copy constructor. ostream_iterator(const ostream_iterator& __obj) @@ -205,7 +215,8 @@ _GLIBCXX_BEGIN_NAMESPACE_VERSION _M_message(__gnu_debug::__msg_output_ostream) ._M_iterator(*this)); *_M_stream << __value; - if (_M_string) *_M_stream << _M_string; + if (_M_string) + *_M_stream << _M_string; return *this; } diff --git a/libstdc++-v3/testsuite/24_iterators/istream_iterator/1.cc b/libstdc++-v3/testsuite/24_iterators/istream_iterator/1.cc new file mode 100644 index 0000000..78b5f7d --- /dev/null +++ b/libstdc++-v3/testsuite/24_iterators/istream_iterator/1.cc @@ -0,0 +1,77 @@ +// Copyright (C) 2019 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 +// . + +// C++98 24.5.1 Template class istream_iterator + +#include +#include +#include +#include + +void test01() +{ + using namespace std; + + const int N = 6; + int arr[N] = { }; + using __gnu_test::test_container; + using __gnu_test::output_iterator_wrapper; + test_container con(arr, arr+N); + output_iterator_wrapper out = con.begin(); + + istringstream ss("1 2 3 4 5"); + std::istream_iterator iter(ss), end; + while (iter != end) + *out++ = *iter++; + VERIFY( iter == end ); + for (int i = 0; i < N; ++i) + VERIFY( arr[i] == (i + 1) % N ); + + std::istream_iterator iter2(ss); + VERIFY( iter2 == end ); + + ss.clear(); + ss.str("-1 -2 -3"); + VERIFY( iter == end ); + +#ifndef _GLIBCXX_DEBUG + // This is undefined, so aborts under debug mode. + // Without debug mode, it should not extract anything from the stream, + // and the iterator should remain at end-of-stream. + ++iter; + VERIFY( iter == end ); +#endif + + std::istream_iterator iter3(ss); + VERIFY( iter3 != end ); + VERIFY( iter3 != iter ); + VERIFY( *iter3 == -1 ); + ++iter3; + VERIFY( *iter3 == -2 ); + + iter2 = iter3; + VERIFY( *iter2 == -2 ); + ++iter2; + VERIFY( *iter2 == -3 ); + ++iter2; + VERIFY( iter2 == end ); +} + +int main() +{ + test01(); +} diff --git a/libstdc++-v3/testsuite/24_iterators/ostream_iterator/1.cc b/libstdc++-v3/testsuite/24_iterators/ostream_iterator/1.cc new file mode 100644 index 0000000..6d12bfb --- /dev/null +++ b/libstdc++-v3/testsuite/24_iterators/ostream_iterator/1.cc @@ -0,0 +1,48 @@ +// Copyright (C) 2016-2019 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 +// . + +// { dg-do compile { target c++11 } } + +#include +#include +#include + +void +test01() +{ + std::ostringstream ss; + std::ostream_iterator iter(ss); + for (int i = 0; i < 5; ++i) + *iter++ = i; + VERIFY( ss.str() == "01234" ); +} + +void +test02() +{ + std::ostringstream ss; + std::ostream_iterator iter(ss, " - "); + for (int i = 0; i < 5; ++i) + *iter++ = i; + VERIFY( ss.str() == "0 - 1 - 2 - 3 - 4 - " ); +} + +int main() +{ + test01(); + test02(); +} diff --git a/libstdc++-v3/testsuite/24_iterators/ostream_iterator/70766.cc b/libstdc++-v3/testsuite/24_iterators/ostream_iterator/70766.cc index cc0aa21..c2e069e 100644 --- a/libstdc++-v3/testsuite/24_iterators/ostream_iterator/70766.cc +++ b/libstdc++-v3/testsuite/24_iterators/ostream_iterator/70766.cc @@ -33,4 +33,5 @@ test01() { std::basic_ostream os(nullptr); std::ostream_iterator oi(os); + std::ostream_iterator oi2(os, ""); } diff --git a/libstdc++-v3/testsuite/24_iterators/ostream_iterator/requirements/constexpr.cc b/libstdc++-v3/testsuite/24_iterators/ostream_iterator/requirements/constexpr.cc new file mode 100644 index 0000000..0fcc8f7 --- /dev/null +++ b/libstdc++-v3/testsuite/24_iterators/ostream_iterator/requirements/constexpr.cc @@ -0,0 +1,24 @@ +// Copyright (C) 2019 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 +// . + +// { dg-options "-std=gnu++2a" } +// { dg-do compile { target c++2a } } + +#include + +constexpr std::ostream_iterator iter1; +constexpr std::ostream_iterator iter2{}; -- 2.7.4