Skip to content

Commit

Permalink
P0896R4 stream iterator changes (#1281)
Browse files Browse the repository at this point in the history
Makes `istream_iterator`, `ostream_iterator`, `istreambuf_iterator`, and `ostreambuf_iterator` model the C++20 iterator concepts.
  • Loading branch information
CaseyCarter authored Oct 9, 2020
1 parent b6437a9 commit 4af65e3
Show file tree
Hide file tree
Showing 6 changed files with 221 additions and 49 deletions.
131 changes: 84 additions & 47 deletions stl/inc/iterator
Original file line number Diff line number Diff line change
Expand Up @@ -227,34 +227,38 @@ private:

// CLASS TEMPLATE istream_iterator
template <class _Ty, class _Elem = char, class _Traits = char_traits<_Elem>, class _Diff = ptrdiff_t>
class istream_iterator { // wrap _Ty extracts from input stream as input iterator
class istream_iterator {
public:
using iterator_category = input_iterator_tag;
using value_type = _Ty;
using difference_type = _Diff;
using pointer = const _Ty*;
using reference = const _Ty&;

using char_type = _Elem;
using traits_type = _Traits;
using istream_type = basic_istream<_Elem, _Traits>;
using char_type = _Elem;
using traits_type = _Traits;
using istream_type = basic_istream<_Elem, _Traits>;

static_assert(conjunction_v<is_default_constructible<_Ty>, is_copy_constructible<_Ty>, is_copy_assignable<_Ty>>,
"istream_iterator<T> requires T to be default constructible, copy constructible, and copy assignable. "
"(N4835 [istream.iterator]/2)");

constexpr istream_iterator() {}
constexpr istream_iterator() noexcept(is_nothrow_default_constructible_v<_Ty>) /* strengthened */ {}

#ifdef __cpp_lib_concepts
constexpr istream_iterator(default_sentinel_t) noexcept(is_nothrow_default_constructible_v<_Ty>) // strengthened
{}
#endif // __cpp_lib_concepts

istream_iterator(istream_type& _Istr) : _Myistr(_STD addressof(_Istr)) {
_Getval();
}

_NODISCARD const _Ty& operator*() const {
_NODISCARD const _Ty& operator*() const noexcept /* strengthened */ {
_STL_ASSERT(_Myistr, "The stored stream pointer in_stream must be non-null");
return _Myval;
}

_NODISCARD const _Ty* operator->() const {
_NODISCARD const _Ty* operator->() const noexcept /* strengthened */ {
_STL_ASSERT(_Myistr, "The stored stream pointer in_stream must be non-null");
return _STD addressof(_Myval);
}
Expand All @@ -270,10 +274,16 @@ public:
return _Tmp;
}

bool _Equal(const istream_iterator& _Right) const {
_NODISCARD bool _Equal(const istream_iterator& _Right) const noexcept {
return _Myistr == _Right._Myistr;
}

#ifdef __cpp_lib_concepts
_NODISCARD friend bool operator==(const istream_iterator& _Left, default_sentinel_t) noexcept /* strengthened */ {
return !_Left._Myistr;
}
#endif // __cpp_lib_concepts

private:
void _Getval() { // get a _Ty value if possible
_STL_ASSERT(_Myistr, "The stored stream pointer in_stream must be non-null");
Expand All @@ -288,31 +298,38 @@ private:

template <class _Ty, class _Elem, class _Traits, class _Diff>
_NODISCARD bool operator==(const istream_iterator<_Ty, _Elem, _Traits, _Diff>& _Left,
const istream_iterator<_Ty, _Elem, _Traits, _Diff>& _Right) {
const istream_iterator<_Ty, _Elem, _Traits, _Diff>& _Right) noexcept /* strengthened */ {
return _Left._Equal(_Right);
}

template <class _Ty, class _Elem, class _Traits, class _Diff>
_NODISCARD bool operator!=(const istream_iterator<_Ty, _Elem, _Traits, _Diff>& _Left,
const istream_iterator<_Ty, _Elem, _Traits, _Diff>& _Right) {
const istream_iterator<_Ty, _Elem, _Traits, _Diff>& _Right) noexcept /* strengthened */ {
return !(_Left == _Right);
}

// CLASS TEMPLATE ostream_iterator
template <class _Ty, class _Elem = char, class _Traits = char_traits<_Elem>>
class ostream_iterator { // wrap _Ty inserts to output stream as output iterator
class ostream_iterator {
public:
using iterator_category = output_iterator_tag;
using value_type = void;
using difference_type = void;
using pointer = void;
using reference = void;

#ifdef __cpp_lib_concepts
using difference_type = ptrdiff_t;
#else
using difference_type = void;
#endif
using pointer = void;
using reference = void;
using char_type = _Elem;
using traits_type = _Traits;
using ostream_type = basic_ostream<_Elem, _Traits>;

ostream_iterator(ostream_type& _Ostr, const _Elem* const _Delim = nullptr)
#ifdef __cpp_lib_concepts
constexpr ostream_iterator() noexcept = default;
#endif // __cpp_lib_concepts

ostream_iterator(ostream_type& _Ostr, const _Elem* const _Delim = nullptr) noexcept /* strengthened */
: _Mydelim(_Delim), _Myostr(_STD addressof(_Ostr)) {}

ostream_iterator& operator=(const _Ty& _Val) { // insert value into output stream, followed by delimiter
Expand All @@ -324,56 +341,59 @@ public:
return *this;
}

_NODISCARD ostream_iterator& operator*() { // pretend to return designated value
_NODISCARD ostream_iterator& operator*() noexcept /* strengthened */ {
return *this;
}

ostream_iterator& operator++() { // pretend to preincrement
ostream_iterator& operator++() noexcept /* strengthened */ {
return *this;
}

ostream_iterator& operator++(int) { // pretend to postincrement
ostream_iterator& operator++(int) noexcept /* strengthened */ {
return *this;
}

protected:
const _Elem* _Mydelim; // pointer to delimiter string (NB: not freed)
ostream_type* _Myostr; // pointer to output stream
private:
const _Elem* _Mydelim = nullptr; // pointer to delimiter string (NB: not freed)
ostream_type* _Myostr = nullptr; // pointer to output stream
};

// CLASS TEMPLATE istreambuf_iterator
template <class _Elem, class _Traits>
class istreambuf_iterator { // wrap stream buffer as input iterator
class istreambuf_iterator {
public:
using iterator_category = input_iterator_tag;
using value_type = _Elem;
using difference_type = typename _Traits::off_type;
using pointer = const _Elem*;
using reference = _Elem;

using char_type = _Elem;
using traits_type = _Traits;
using streambuf_type = basic_streambuf<_Elem, _Traits>;
using istream_type = basic_istream<_Elem, _Traits>;

using int_type = typename traits_type::int_type;
using char_type = _Elem;
using traits_type = _Traits;
using int_type = typename traits_type::int_type;
using streambuf_type = basic_streambuf<_Elem, _Traits>;
using istream_type = basic_istream<_Elem, _Traits>;

constexpr istreambuf_iterator() noexcept : _Strbuf(nullptr), _Got(true), _Val() {}

istreambuf_iterator(streambuf_type* _Sb) noexcept : _Strbuf(_Sb), _Got(!_Sb), _Val() {}
#ifdef __cpp_lib_concepts
constexpr istreambuf_iterator(default_sentinel_t) noexcept : _Strbuf(nullptr), _Got(true), _Val() {}
#endif // __cpp_lib_concepts

istreambuf_iterator(istream_type& _Istr) noexcept : _Strbuf(_Istr.rdbuf()), _Got(!_Strbuf), _Val() {}

istreambuf_iterator(streambuf_type* _Sb) noexcept : _Strbuf(_Sb), _Got(!_Sb), _Val() {}

private:
class _Istreambuf_proxy {
public:
_NODISCARD _Elem operator*() const {
_NODISCARD _Elem operator*() const noexcept(is_nothrow_copy_constructible_v<_Elem>) /* strengthened */ {
return _Keep;
}

private:
friend istreambuf_iterator;
_Istreambuf_proxy(streambuf_type* _Strbuf_, _Elem _Keep_) : _Strbuf(_Strbuf_), _Keep(_Keep_) {}
_Istreambuf_proxy(streambuf_type* _Strbuf_, _Elem _Keep_) noexcept(
is_nothrow_copy_constructible_v<_Elem>) // strengthened
: _Strbuf(_Strbuf_), _Keep(_Keep_) {}

streambuf_type* _Strbuf;
_Elem _Keep;
Expand Down Expand Up @@ -425,6 +445,16 @@ public:
return (!_Strbuf && !_Right._Strbuf) || (_Strbuf && _Right._Strbuf);
}

#ifdef __cpp_lib_concepts
_NODISCARD friend bool operator==(const istreambuf_iterator& _Left, default_sentinel_t) {
if (!_Left._Got) {
_Left._Peek();
}

return !_Left._Strbuf;
}
#endif // __cpp_lib_concepts

private:
void _Inc() { // skip to next input element
if (!_Strbuf || traits_type::eq_int_type(traits_type::eof(), _Strbuf->sbumpc())) {
Expand Down Expand Up @@ -466,22 +496,29 @@ _NODISCARD bool operator!=(

// CLASS TEMPLATE ostreambuf_iterator
template <class _Elem, class _Traits>
class ostreambuf_iterator { // wrap stream buffer as output iterator
class ostreambuf_iterator {
public:
using iterator_category = output_iterator_tag;
using value_type = void;
using difference_type = void;
using pointer = void;
using reference = void;

#ifdef __cpp_lib_concepts
using difference_type = ptrdiff_t;
#else
using difference_type = void;
#endif
using pointer = void;
using reference = void;
using char_type = _Elem;
using traits_type = _Traits;
using streambuf_type = basic_streambuf<_Elem, _Traits>;
using ostream_type = basic_ostream<_Elem, _Traits>;

ostreambuf_iterator(streambuf_type* _Sb) noexcept : _Failed(false), _Strbuf(_Sb) {}
#ifdef __cpp_lib_concepts
constexpr ostreambuf_iterator() noexcept = default;
#endif // __cpp_lib_concepts

ostreambuf_iterator(streambuf_type* _Sb) noexcept : _Strbuf(_Sb) {}

ostreambuf_iterator(ostream_type& _Ostr) noexcept : _Failed(false), _Strbuf(_Ostr.rdbuf()) {}
ostreambuf_iterator(ostream_type& _Ostr) noexcept : _Strbuf(_Ostr.rdbuf()) {}

ostreambuf_iterator& operator=(_Elem _Right) { // store element and increment
if (!_Strbuf || traits_type::eq_int_type(_Traits::eof(), _Strbuf->sputc(_Right))) {
Expand All @@ -491,15 +528,15 @@ public:
return *this;
}

_NODISCARD ostreambuf_iterator& operator*() { // pretend to get designated element
_NODISCARD ostreambuf_iterator& operator*() noexcept /* strengthened */ {
return *this;
}

ostreambuf_iterator& operator++() { // pretend to preincrement
ostreambuf_iterator& operator++() noexcept /* strengthened */ {
return *this;
}

ostreambuf_iterator& operator++(int) { // pretend to postincrement
ostreambuf_iterator& operator++(int) noexcept /* strengthened */ {
return *this;
}

Expand All @@ -508,8 +545,8 @@ public:
}

private:
bool _Failed; // true if any stores have failed
streambuf_type* _Strbuf; // the wrapped stream buffer
bool _Failed = false; // true if any stores have failed
streambuf_type* _Strbuf = nullptr;
};

#ifdef __cpp_lib_concepts
Expand Down
4 changes: 3 additions & 1 deletion tests/libcxx/expected_results.txt
Original file line number Diff line number Diff line change
Expand Up @@ -742,10 +742,12 @@ std/language.support/cmp/cmp.weakord/weakord.pass.cpp FAIL
# error C4576: a parenthesized type followed by an initializer list is a non-standard explicit type conversion syntax
std/containers/sequences/array/array.creation/to_array.pass.cpp:0 FAIL

# Tests that need to learn that insert iterators have non-void difference type in C++20
# Tests that need to learn that iterators have non-void difference types in C++20
std/iterators/predef.iterators/insert.iterators/back.insert.iterator/types.pass.cpp FAIL
std/iterators/predef.iterators/insert.iterators/front.insert.iterator/types.pass.cpp FAIL
std/iterators/predef.iterators/insert.iterators/insert.iterator/types.pass.cpp FAIL
std/iterators/stream.iterators/ostream.iterator/types.pass.cpp FAIL
std/iterators/stream.iterators/ostreambuf.iterator/types.pass.cpp FAIL

# Tests emit warning C4244: 'argument': conversion from 'T' to 'const std::complex<double>::_Ty', possible loss of data
std/numerics/complex.number/cmplx.over/conj.pass.cpp:0 FAIL
Expand Down
4 changes: 3 additions & 1 deletion tests/libcxx/skipped_tests.txt
Original file line number Diff line number Diff line change
Expand Up @@ -742,10 +742,12 @@ language.support\cmp\cmp.weakord\weakord.pass.cpp
# error C4576: a parenthesized type followed by an initializer list is a non-standard explicit type conversion syntax
containers\sequences\array\array.creation\to_array.pass.cpp

# Tests that need to learn that insert iterators have non-void difference type in C++20
# Tests that need to learn that iterators have non-void difference types in C++20
iterators\predef.iterators\insert.iterators\back.insert.iterator\types.pass.cpp
iterators\predef.iterators\insert.iterators\front.insert.iterator\types.pass.cpp
iterators\predef.iterators\insert.iterators\insert.iterator\types.pass.cpp
iterators\stream.iterators\ostream.iterator\types.pass.cpp
iterators\stream.iterators\ostreambuf.iterator\types.pass.cpp

# Tests emit warning C4244: 'argument': conversion from 'T' to 'const std::complex<double>::_Ty', possible loss of data
numerics\complex.number\cmplx.over\conj.pass.cpp
Expand Down
1 change: 1 addition & 0 deletions tests/std/test.lst
Original file line number Diff line number Diff line change
Expand Up @@ -336,6 +336,7 @@ tests\P0896R4_ranges_ref_view
tests\P0896R4_ranges_subrange
tests\P0896R4_ranges_test_machinery
tests\P0896R4_ranges_to_address
tests\P0896R4_stream_iterators
tests\P0896R4_views_all
tests\P0896R4_views_drop
tests\P0896R4_views_empty
Expand Down
4 changes: 4 additions & 0 deletions tests/std/tests/P0896R4_stream_iterators/env.lst
Original file line number Diff line number Diff line change
@@ -0,0 +1,4 @@
# Copyright (c) Microsoft Corporation.
# SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception

RUNALL_INCLUDE ..\usual_matrix.lst
Loading

0 comments on commit 4af65e3

Please sign in to comment.