From b8d3ca38a108e9fe39df66aa7ae35ed22e67da01 Mon Sep 17 00:00:00 2001 From: Michael Schellenberger Costa Date: Thu, 1 Oct 2020 23:46:43 +0200 Subject: [PATCH 1/8] Implement ranges::istream_view --- stl/inc/ranges | 84 +++++++++++++++++ tests/std/test.lst | 2 + tests/std/tests/P0896R4_views_istream/env.lst | 4 + .../std/tests/P0896R4_views_istream/test.cpp | 89 +++++++++++++++++++ .../tests/P0896R4_views_istream_death/env.lst | 4 + .../P0896R4_views_istream_death/test.cpp | 89 +++++++++++++++++++ 6 files changed, 272 insertions(+) create mode 100644 tests/std/tests/P0896R4_views_istream/env.lst create mode 100644 tests/std/tests/P0896R4_views_istream/test.cpp create mode 100644 tests/std/tests/P0896R4_views_istream_death/env.lst create mode 100644 tests/std/tests/P0896R4_views_istream_death/test.cpp diff --git a/stl/inc/ranges b/stl/inc/ranges index 0abfedeb742..5d431ca2605 100644 --- a/stl/inc/ranges +++ b/stl/inc/ranges @@ -11,6 +11,7 @@ #ifndef __cpp_lib_concepts #pragma message("The contents of are available only with C++20 concepts support.") #else // ^^^ !defined(__cpp_lib_concepts) / defined(__cpp_lib_concepts) vvv +#include #include #include #include @@ -624,6 +625,89 @@ namespace ranges { inline constexpr _Single_fn single; } // namespace views + // CLASS TEMPLATE ranges::istream_view + template + concept _Stream_extractable = requires(basic_istream<_Elem, _Traits>& __is, _Ty& __t) { + __is >> __t; + }; + + // clang-format off + template > + requires default_initializable<_Ty> && _Stream_extractable<_Ty, _Elem, _Traits> + class basic_istream_view : public view_interface> { + // clang-format on + private: + class _Iterator { + private: + basic_istream_view* _Parent = nullptr; + + public: + using iterator_concept = input_iterator_tag; + using difference_type = ptrdiff_t; + using value_type = _Ty; + + _Iterator() = default; + constexpr explicit _Iterator(basic_istream_view& _Parent_) noexcept : _Parent{_STD addressof(_Parent_)} {} + + _Iterator(const _Iterator&) = delete; + _Iterator(_Iterator&&) = default; + + _Iterator& operator=(const _Iterator&) = delete; + _Iterator& operator=(_Iterator&&) = default; + + _Iterator& operator++() { +#if _ITERATOR_DEBUG_LEVEL != 0 + _STL_VERIFY(*this != default_sentinel, "cannot increment istream_view iterator at end of stream"); +#endif // _ITERATOR_DEBUG_LEVEL != 0 + *_Parent->_Stream >> _Parent->_Val; + return *this; + } + + void operator++(int) { + ++*this; + } + + _Ty& operator*() const { +#if _ITERATOR_DEBUG_LEVEL != 0 + _STL_VERIFY(*this != default_sentinel, "cannot dereference istream_view iterator at end of stream"); +#endif // _ITERATOR_DEBUG_LEVEL != 0 + return _Parent->_Val; + } + + friend bool operator==(const _Iterator& _Left, default_sentinel_t) { + return _Left._Parent == nullptr || _Left._Parent->_Stream_at_end(); + } + }; + + basic_istream<_Elem, _Traits>* _Stream = nullptr; + _Ty _Val = _Ty{}; + + public: + basic_istream_view() = default; + constexpr explicit basic_istream_view(basic_istream<_Elem, _Traits>& _Stream_) noexcept // strengthened + : _Stream{_STD addressof(_Stream_)} {} + + constexpr auto begin() { + if (_Stream) { + *_Stream >> _Val; + } + return _Iterator{*this}; + } + + constexpr default_sentinel_t end() const noexcept { + return default_sentinel; + } + + constexpr bool _Stream_at_end() const noexcept { + return _Stream == nullptr || !*_Stream; + } + }; + + template + basic_istream_view<_Ty, _Elem, _Traits> istream_view(basic_istream<_Elem, _Traits>& _Stream) { + return basic_istream_view<_Ty, _Elem, _Traits>{_Stream}; + } + // CLASS TEMPLATE ranges::ref_view // clang-format off template diff --git a/tests/std/test.lst b/tests/std/test.lst index 84533ac11d0..902e0eecaf7 100644 --- a/tests/std/test.lst +++ b/tests/std/test.lst @@ -339,6 +339,8 @@ tests\P0896R4_views_drop tests\P0896R4_views_empty tests\P0896R4_views_filter tests\P0896R4_views_filter_death +tests\P0896R4_views_istream +tests\P0896R4_views_istream_death tests\P0896R4_views_reverse tests\P0896R4_views_single tests\P0896R4_views_take diff --git a/tests/std/tests/P0896R4_views_istream/env.lst b/tests/std/tests/P0896R4_views_istream/env.lst new file mode 100644 index 00000000000..f3ccc8613c6 --- /dev/null +++ b/tests/std/tests/P0896R4_views_istream/env.lst @@ -0,0 +1,4 @@ +# Copyright (c) Microsoft Corporation. +# SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception + +RUNALL_INCLUDE ..\concepts_matrix.lst diff --git a/tests/std/tests/P0896R4_views_istream/test.cpp b/tests/std/tests/P0896R4_views_istream/test.cpp new file mode 100644 index 00000000000..2d35fed60ea --- /dev/null +++ b/tests/std/tests/P0896R4_views_istream/test.cpp @@ -0,0 +1,89 @@ +// Copyright (c) Microsoft Corporation. +// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception + +#include +#include +#include +#include +#include + +#include + +using namespace std; + +static constexpr int expected_empty[] = {-1, -1, -1, -1, -1}; +static constexpr int expected_copied[] = {0, 1, 2, 3, -1}; + +struct streamable { + streamable() = default; + streamable(const int input) : _val(input) {} + + friend basic_istream& operator>>(basic_istream& is, streamable& right) noexcept { + is >> right._val; + return is; + } + + friend bool operator==(const streamable& left, const streamable& right) noexcept = default; + + int _val = 0; +}; + +template +bool test_one_type() { + using ranges::basic_istream_view; + + // validate type properties + using R = basic_istream_view; + static_assert(ranges::view); + static_assert(ranges::input_range); + static_assert(!ranges::forward_range); + + static_assert(!ranges::sized_range); + static_assert(!ranges::common_range); + + // validate constructor + auto nonempty_stream = istringstream{"0"}; + auto empty_intstream = istringstream{}; + same_as auto default_constructed = ranges::basic_istream_view{}; + same_as auto empty_constructed = ranges::basic_istream_view{empty_intstream}; + same_as auto value_constructed = ranges::basic_istream_view{nonempty_stream}; + + // validate member begin + // NOTE: This moves the stream one element int front + assert(default_constructed.begin() == default_sentinel); + assert(empty_constructed.begin() == default_sentinel); + assert(value_constructed.begin() != default_sentinel); + + // validate member end + static_assert(same_as); + static_assert(noexcept(default_constructed.end())); + static_assert(noexcept(ranges::end(default_constructed))); + + // Non existing member functions + static_assert(!CanMemberSize); + static_assert(!CanMemberData); + static_assert(!CanMemberEmpty); + static_assert(!CanMemberFront); + static_assert(!CanMemberBack); + + // Some basic test + T input_default[] = {-1, -1, -1, -1, -1}; + ranges::copy(default_constructed, input_default); + assert(ranges::equal(input_default, expected_empty)); + + T input_empty[] = {-1, -1, -1, -1, -1}; + ranges::copy(empty_constructed, input_empty); + assert(ranges::equal(input_empty, expected_empty)); + + auto intstream = istringstream{"0 1 2 3"}; + T input_value[] = {-1, -1, -1, -1, -1}; + ranges::copy(ranges::basic_istream_view{intstream}, input_value); + assert(ranges::equal(input_value, expected_copied)); + + return true; +} + +int main() { + test_one_type(); + test_one_type(); +} diff --git a/tests/std/tests/P0896R4_views_istream_death/env.lst b/tests/std/tests/P0896R4_views_istream_death/env.lst new file mode 100644 index 00000000000..22f1f0230a4 --- /dev/null +++ b/tests/std/tests/P0896R4_views_istream_death/env.lst @@ -0,0 +1,4 @@ +# Copyright (c) Microsoft Corporation. +# SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception + +RUNALL_INCLUDE ..\strict_winsdk_concepts_matrix.lst diff --git a/tests/std/tests/P0896R4_views_istream_death/test.cpp b/tests/std/tests/P0896R4_views_istream_death/test.cpp new file mode 100644 index 00000000000..1ee68d3dac5 --- /dev/null +++ b/tests/std/tests/P0896R4_views_istream_death/test.cpp @@ -0,0 +1,89 @@ +// Copyright (c) Microsoft Corporation. +// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception + +#define _CONTAINER_DEBUG_LEVEL 1 + +#include +#include +#include +#include + +#include +using namespace std; + +using iview = ranges::basic_istream_view; + +void test_predecrement_default_initialized() { + decltype(std::declval().begin()) it; + (void) ++it; +} + +void test_postdecrement_default_initialized() { + decltype(std::declval().begin()) it; + (void) it++; +} + +void test_dereference_default_initialized() { + decltype(std::declval().begin()) it; + (void) *it; +} + +void test_predecrement_no_stream() { + iview v; + auto it = v.begin(); + (void) ++it; +} + +void test_postdecrement_no_stream() { + iview v; + auto it = v.begin(); + (void) it++; +} + +void test_dereference_no_stream() { + iview v; + auto it = v.begin(); + (void) *it; +} + + +void test_predecrement_end_of_stream() { + istringstream stream; + iview view{stream}; + auto it = view.begin(); + (void) ++it; +} + +void test_postdecrement_end_of_stream() { + istringstream stream; + iview view{stream}; + auto it = view.begin(); + (void) it++; +} + +void test_dereference_end_of_stream() { + istringstream stream; + iview view{stream}; + auto it = view.begin(); + (void) *it; +} + +int main(int argc, char* argv[]) { + std_testing::death_test_executive exec([] {}); + +#if _ITERATOR_DEBUG_LEVEL != 0 + exec.add_death_tests({ + test_predecrement_default_initialized, + test_postdecrement_default_initialized, + test_dereference_default_initialized, + test_predecrement_no_stream, + test_postdecrement_no_stream, + test_dereference_no_stream, + test_predecrement_end_of_stream, + test_postdecrement_end_of_stream, + test_dereference_end_of_stream, + }); +#endif // _ITERATOR_DEBUG_LEVEL != 0 + + return exec.run(argc, argv); +} From d2554d1231d164d1d9bed312155630b093c58cd5 Mon Sep 17 00:00:00 2001 From: Michael Schellenberger Costa Date: Fri, 2 Oct 2020 09:30:58 +0200 Subject: [PATCH 2/8] Address review comments --- stl/inc/ranges | 29 +++++++++----- .../std/tests/P0896R4_views_istream/test.cpp | 38 ++++++++++++------- .../P0896R4_views_istream_death/test.cpp | 31 ++++++++------- 3 files changed, 59 insertions(+), 39 deletions(-) diff --git a/stl/inc/ranges b/stl/inc/ranges index 5d431ca2605..ba8b74dbc87 100644 --- a/stl/inc/ranges +++ b/stl/inc/ranges @@ -657,7 +657,12 @@ namespace ranges { _Iterator& operator++() { #if _ITERATOR_DEBUG_LEVEL != 0 - _STL_VERIFY(*this != default_sentinel, "cannot increment istream_view iterator at end of stream"); + _STL_VERIFY(_Parent != nullptr, + "cannot increment default initialized istream_view iterator"); // Per LWG-XXXX + _STL_VERIFY( + _Parent->_Stream != nullptr, "cannot increment istream_view iterator with uninitialized stream"); + _STL_VERIFY(!_Parent->_Stream_at_end(), + "cannot increment istream_view iterator at end of stream"); // Per LWG-XXXX #endif // _ITERATOR_DEBUG_LEVEL != 0 *_Parent->_Stream >> _Parent->_Val; return *this; @@ -667,14 +672,19 @@ namespace ranges { ++*this; } - _Ty& operator*() const { + _NODISCARD _Ty& operator*() const noexcept /* strengthened */ { #if _ITERATOR_DEBUG_LEVEL != 0 - _STL_VERIFY(*this != default_sentinel, "cannot dereference istream_view iterator at end of stream"); + _STL_VERIFY(_Parent != nullptr, + "cannot dereference default initialized istream_view iterator"); // Per LWG-XXXX + _STL_VERIFY( + _Parent->_Stream != nullptr, "cannot dereference istream_view iterator with uninitialized stream"); + _STL_VERIFY(!_Parent->_Stream_at_end(), + "cannot dereference istream_view iterator at end of stream"); // Per LWG-XXXX #endif // _ITERATOR_DEBUG_LEVEL != 0 return _Parent->_Val; } - friend bool operator==(const _Iterator& _Left, default_sentinel_t) { + _NODISCARD friend bool operator==(const _Iterator& _Left, default_sentinel_t) noexcept /* strengthened */ { return _Left._Parent == nullptr || _Left._Parent->_Stream_at_end(); } }; @@ -684,27 +694,28 @@ namespace ranges { public: basic_istream_view() = default; - constexpr explicit basic_istream_view(basic_istream<_Elem, _Traits>& _Stream_) noexcept // strengthened + constexpr explicit basic_istream_view(basic_istream<_Elem, _Traits>& _Stream_) noexcept( + is_nothrow_default_constructible_v<_Ty>) // strengthened : _Stream{_STD addressof(_Stream_)} {} - constexpr auto begin() { + _NODISCARD constexpr auto begin() { if (_Stream) { *_Stream >> _Val; } return _Iterator{*this}; } - constexpr default_sentinel_t end() const noexcept { + _NODISCARD constexpr default_sentinel_t end() const noexcept { return default_sentinel; } - constexpr bool _Stream_at_end() const noexcept { + _NODISCARD constexpr bool _Stream_at_end() const noexcept { return _Stream == nullptr || !*_Stream; } }; template - basic_istream_view<_Ty, _Elem, _Traits> istream_view(basic_istream<_Elem, _Traits>& _Stream) { + _NODISCARD basic_istream_view<_Ty, _Elem, _Traits> istream_view(basic_istream<_Elem, _Traits>& _Stream) { return basic_istream_view<_Ty, _Elem, _Traits>{_Stream}; } diff --git a/tests/std/tests/P0896R4_views_istream/test.cpp b/tests/std/tests/P0896R4_views_istream/test.cpp index 2d35fed60ea..a8f09acf3ec 100644 --- a/tests/std/tests/P0896R4_views_istream/test.cpp +++ b/tests/std/tests/P0896R4_views_istream/test.cpp @@ -4,6 +4,7 @@ #include #include #include +#include #include #include @@ -11,8 +12,7 @@ using namespace std; -static constexpr int expected_empty[] = {-1, -1, -1, -1, -1}; -static constexpr int expected_copied[] = {0, 1, 2, 3, -1}; +constexpr int expected_empty[] = {-1, -1, -1, -1, -1}; struct streamable { streamable() = default; @@ -29,7 +29,7 @@ struct streamable { }; template -bool test_one_type() { +constexpr bool test_one_type() { using ranges::basic_istream_view; // validate type properties @@ -42,17 +42,21 @@ bool test_one_type() { static_assert(!ranges::common_range); // validate constructor - auto nonempty_stream = istringstream{"0"}; - auto empty_intstream = istringstream{}; - same_as auto default_constructed = ranges::basic_istream_view{}; - same_as auto empty_constructed = ranges::basic_istream_view{empty_intstream}; - same_as auto value_constructed = ranges::basic_istream_view{nonempty_stream}; + auto nonempty_stream = istringstream{"0"}; + auto empty_intstream = istringstream{}; + same_as auto default_constructed = basic_istream_view{}; + same_as auto empty_constructed = basic_istream_view{empty_intstream}; + same_as auto non_empty_constructed = basic_istream_view{nonempty_stream}; // validate member begin - // NOTE: This moves the stream one element int front + // NOTE: begin() consumes the first token assert(default_constructed.begin() == default_sentinel); assert(empty_constructed.begin() == default_sentinel); - assert(value_constructed.begin() != default_sentinel); + assert(non_empty_constructed.begin() != default_sentinel); + + // validate default constructed istream::iterator + const auto default_constructed_it = ranges::iterator_t(); + assert(default_constructed_it == default_sentinel); // validate member end static_assert(same_as); @@ -75,10 +79,16 @@ bool test_one_type() { ranges::copy(empty_constructed, input_empty); assert(ranges::equal(input_empty, expected_empty)); - auto intstream = istringstream{"0 1 2 3"}; - T input_value[] = {-1, -1, -1, -1, -1}; - ranges::copy(ranges::basic_istream_view{intstream}, input_value); - assert(ranges::equal(input_value, expected_copied)); + const T expected[] = {0, 1, 2, 3, -1}; + auto intstream = istringstream{"0 1 2 3"}; + T input_value[] = {-1, -1, -1, -1, -1}; + ranges::copy(basic_istream_view{intstream}, input_value); + assert(ranges::equal(input_value, expected)); + + auto intstream_view = istringstream{"0 1 2 3"}; + T input_value_view[] = {-1, -1, -1, -1, -1}; + ranges::copy(ranges::istream_view(intstream_view), input_value_view); + assert(ranges::equal(input_value_view, expected)); return true; } diff --git a/tests/std/tests/P0896R4_views_istream_death/test.cpp b/tests/std/tests/P0896R4_views_istream_death/test.cpp index 1ee68d3dac5..004652c4738 100644 --- a/tests/std/tests/P0896R4_views_istream_death/test.cpp +++ b/tests/std/tests/P0896R4_views_istream_death/test.cpp @@ -13,28 +13,28 @@ using namespace std; using iview = ranges::basic_istream_view; -void test_predecrement_default_initialized() { - decltype(std::declval().begin()) it; +void test_preincrement_default_initialized() { + ranges::iterator_t it; (void) ++it; } -void test_postdecrement_default_initialized() { - decltype(std::declval().begin()) it; +void test_postincrement_default_initialized() { + ranges::iterator_t it; (void) it++; } void test_dereference_default_initialized() { - decltype(std::declval().begin()) it; + ranges::iterator_t it; (void) *it; } -void test_predecrement_no_stream() { +void test_preincrement_no_stream() { iview v; auto it = v.begin(); (void) ++it; } -void test_postdecrement_no_stream() { +void test_postincrement_no_stream() { iview v; auto it = v.begin(); (void) it++; @@ -46,15 +46,14 @@ void test_dereference_no_stream() { (void) *it; } - -void test_predecrement_end_of_stream() { +void test_preincrement_end_of_stream() { istringstream stream; iview view{stream}; auto it = view.begin(); (void) ++it; } -void test_postdecrement_end_of_stream() { +void test_postincrement_end_of_stream() { istringstream stream; iview view{stream}; auto it = view.begin(); @@ -73,14 +72,14 @@ int main(int argc, char* argv[]) { #if _ITERATOR_DEBUG_LEVEL != 0 exec.add_death_tests({ - test_predecrement_default_initialized, - test_postdecrement_default_initialized, + test_preincrement_default_initialized, + test_postincrement_default_initialized, test_dereference_default_initialized, - test_predecrement_no_stream, - test_postdecrement_no_stream, + test_preincrement_no_stream, + test_postincrement_no_stream, test_dereference_no_stream, - test_predecrement_end_of_stream, - test_postdecrement_end_of_stream, + test_preincrement_end_of_stream, + test_postincrement_end_of_stream, test_dereference_end_of_stream, }); #endif // _ITERATOR_DEBUG_LEVEL != 0 From 769bb066104edd0917a19f7070d0b25a77b31de6 Mon Sep 17 00:00:00 2001 From: Michael Schellenberger Costa Date: Fri, 2 Oct 2020 12:24:40 +0200 Subject: [PATCH 3/8] No constexpr for you sir! --- tests/std/tests/P0896R4_views_istream/test.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/tests/std/tests/P0896R4_views_istream/test.cpp b/tests/std/tests/P0896R4_views_istream/test.cpp index a8f09acf3ec..ccc9ae46de5 100644 --- a/tests/std/tests/P0896R4_views_istream/test.cpp +++ b/tests/std/tests/P0896R4_views_istream/test.cpp @@ -29,7 +29,7 @@ struct streamable { }; template -constexpr bool test_one_type() { +bool test_one_type() { using ranges::basic_istream_view; // validate type properties From 7257687cd63a147e6f44ade7ab1b4d28b785f520 Mon Sep 17 00:00:00 2001 From: Michael Schellenberger Costa Date: Mon, 5 Oct 2020 22:38:59 +0200 Subject: [PATCH 4/8] Use istream rather than basic_istream --- tests/std/tests/P0896R4_views_istream/test.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/tests/std/tests/P0896R4_views_istream/test.cpp b/tests/std/tests/P0896R4_views_istream/test.cpp index ccc9ae46de5..0912ad492e0 100644 --- a/tests/std/tests/P0896R4_views_istream/test.cpp +++ b/tests/std/tests/P0896R4_views_istream/test.cpp @@ -18,7 +18,7 @@ struct streamable { streamable() = default; streamable(const int input) : _val(input) {} - friend basic_istream& operator>>(basic_istream& is, streamable& right) noexcept { + friend istream& operator>>(istream& is, streamable& right) noexcept { is >> right._val; return is; } From a978ef88caf61fe11d54abf6f23680116fe89955 Mon Sep 17 00:00:00 2001 From: Casey Carter Date: Mon, 12 Oct 2020 16:33:22 -0700 Subject: [PATCH 5/8] Casey's review comments * Include the number of LWG-3489 * Rename test folders from `views_istream` to `istream_view` since there is no `std::ranges::view::istream` * Minimal constexpr test coverage * Remove behavior extension that makes default-constructed `basic_istream_view` model range --- stl/inc/ranges | 19 +++--- tests/std/test.lst | 4 +- .../env.lst | 0 .../test.cpp | 62 ++++++++++++------- .../env.lst | 0 .../test.cpp | 8 +++ 6 files changed, 60 insertions(+), 33 deletions(-) rename tests/std/tests/{P0896R4_views_istream => P0896R4_istream_view}/env.lst (100%) rename tests/std/tests/{P0896R4_views_istream => P0896R4_istream_view}/test.cpp (57%) rename tests/std/tests/{P0896R4_views_istream_death => P0896R4_istream_view_death}/env.lst (100%) rename tests/std/tests/{P0896R4_views_istream_death => P0896R4_istream_view_death}/test.cpp (88%) diff --git a/stl/inc/ranges b/stl/inc/ranges index ba8b74dbc87..ca1f75bfe8f 100644 --- a/stl/inc/ranges +++ b/stl/inc/ranges @@ -657,12 +657,11 @@ namespace ranges { _Iterator& operator++() { #if _ITERATOR_DEBUG_LEVEL != 0 - _STL_VERIFY(_Parent != nullptr, - "cannot increment default initialized istream_view iterator"); // Per LWG-XXXX + // Per LWG-3489 + _STL_VERIFY(_Parent != nullptr, "cannot increment default initialized istream_view iterator"); _STL_VERIFY( _Parent->_Stream != nullptr, "cannot increment istream_view iterator with uninitialized stream"); - _STL_VERIFY(!_Parent->_Stream_at_end(), - "cannot increment istream_view iterator at end of stream"); // Per LWG-XXXX + _STL_VERIFY(!_Parent->_Stream_at_end(), "cannot increment istream_view iterator at end of stream"); #endif // _ITERATOR_DEBUG_LEVEL != 0 *_Parent->_Stream >> _Parent->_Val; return *this; @@ -674,12 +673,11 @@ namespace ranges { _NODISCARD _Ty& operator*() const noexcept /* strengthened */ { #if _ITERATOR_DEBUG_LEVEL != 0 - _STL_VERIFY(_Parent != nullptr, - "cannot dereference default initialized istream_view iterator"); // Per LWG-XXXX + // Per LWG-3489 + _STL_VERIFY(_Parent != nullptr, "cannot dereference default initialized istream_view iterator"); _STL_VERIFY( _Parent->_Stream != nullptr, "cannot dereference istream_view iterator with uninitialized stream"); - _STL_VERIFY(!_Parent->_Stream_at_end(), - "cannot dereference istream_view iterator at end of stream"); // Per LWG-XXXX + _STL_VERIFY(!_Parent->_Stream_at_end(), "cannot dereference istream_view iterator at end of stream"); #endif // _ITERATOR_DEBUG_LEVEL != 0 return _Parent->_Val; } @@ -710,12 +708,13 @@ namespace ranges { } _NODISCARD constexpr bool _Stream_at_end() const noexcept { - return _Stream == nullptr || !*_Stream; + return !*_Stream; } }; template - _NODISCARD basic_istream_view<_Ty, _Elem, _Traits> istream_view(basic_istream<_Elem, _Traits>& _Stream) { + _NODISCARD basic_istream_view<_Ty, _Elem, _Traits> istream_view(basic_istream<_Elem, _Traits>& _Stream) noexcept( + is_nothrow_default_constructible_v<_Ty>) /* strengthened */ { return basic_istream_view<_Ty, _Elem, _Traits>{_Stream}; } diff --git a/tests/std/test.lst b/tests/std/test.lst index 902e0eecaf7..51d2794ef25 100644 --- a/tests/std/test.lst +++ b/tests/std/test.lst @@ -339,8 +339,8 @@ tests\P0896R4_views_drop tests\P0896R4_views_empty tests\P0896R4_views_filter tests\P0896R4_views_filter_death -tests\P0896R4_views_istream -tests\P0896R4_views_istream_death +tests\P0896R4_istream_view +tests\P0896R4_istream_view_death tests\P0896R4_views_reverse tests\P0896R4_views_single tests\P0896R4_views_take diff --git a/tests/std/tests/P0896R4_views_istream/env.lst b/tests/std/tests/P0896R4_istream_view/env.lst similarity index 100% rename from tests/std/tests/P0896R4_views_istream/env.lst rename to tests/std/tests/P0896R4_istream_view/env.lst diff --git a/tests/std/tests/P0896R4_views_istream/test.cpp b/tests/std/tests/P0896R4_istream_view/test.cpp similarity index 57% rename from tests/std/tests/P0896R4_views_istream/test.cpp rename to tests/std/tests/P0896R4_istream_view/test.cpp index 0912ad492e0..cfcd7da9f66 100644 --- a/tests/std/tests/P0896R4_views_istream/test.cpp +++ b/tests/std/tests/P0896R4_istream_view/test.cpp @@ -4,7 +4,7 @@ #include #include #include -#include +#include #include #include @@ -13,6 +13,7 @@ using namespace std; constexpr int expected_empty[] = {-1, -1, -1, -1, -1}; +constexpr int expected[] = {0, 1, 2, 3, -1}; struct streamable { streamable() = default; @@ -29,7 +30,7 @@ struct streamable { }; template -bool test_one_type() { +void test_one_type() { using ranges::basic_istream_view; // validate type properties @@ -41,22 +42,28 @@ bool test_one_type() { static_assert(!ranges::sized_range); static_assert(!ranges::common_range); - // validate constructor - auto nonempty_stream = istringstream{"0"}; - auto empty_intstream = istringstream{}; - same_as auto default_constructed = basic_istream_view{}; - same_as auto empty_constructed = basic_istream_view{empty_intstream}; - same_as auto non_empty_constructed = basic_istream_view{nonempty_stream}; + // validate constructors + istringstream nonempty_stream{"0"}; + istringstream empty_intstream{}; + R default_constructed{}; + R empty_constructed{empty_intstream}; + R non_empty_constructed{nonempty_stream}; + + static_assert(is_nothrow_constructible_v == is_nothrow_default_constructible_v); + static_assert(is_nothrow_constructible_v == is_nothrow_default_constructible_v); // validate member begin // NOTE: begin() consumes the first token - assert(default_constructed.begin() == default_sentinel); + (void) default_constructed.begin(); // default-constructed basic_istream_view doesn't model range. assert(empty_constructed.begin() == default_sentinel); assert(non_empty_constructed.begin() != default_sentinel); // validate default constructed istream::iterator - const auto default_constructed_it = ranges::iterator_t(); - assert(default_constructed_it == default_sentinel); + { + const ranges::iterator_t default_constructed_it; + assert(default_constructed_it == default_sentinel); + static_assert(noexcept(default_constructed_it == default_sentinel)); + } // validate member end static_assert(same_as); @@ -70,25 +77,36 @@ bool test_one_type() { static_assert(!CanMemberFront); static_assert(!CanMemberBack); - // Some basic test - T input_default[] = {-1, -1, -1, -1, -1}; - ranges::copy(default_constructed, input_default); - assert(ranges::equal(input_default, expected_empty)); - + // Some basic tests T input_empty[] = {-1, -1, -1, -1, -1}; ranges::copy(empty_constructed, input_empty); assert(ranges::equal(input_empty, expected_empty)); - const T expected[] = {0, 1, 2, 3, -1}; - auto intstream = istringstream{"0 1 2 3"}; - T input_value[] = {-1, -1, -1, -1, -1}; + istringstream intstream{"0 1 2 3"}; + T input_value[] = {-1, -1, -1, -1, -1}; ranges::copy(basic_istream_view{intstream}, input_value); assert(ranges::equal(input_value, expected)); - auto intstream_view = istringstream{"0 1 2 3"}; + istringstream intstream_view{"0 1 2 3"}; T input_value_view[] = {-1, -1, -1, -1, -1}; - ranges::copy(ranges::istream_view(intstream_view), input_value_view); + ranges::copy(ranges::istream_view(intstream_view), input_value_view); + static_assert(noexcept(ranges::istream_view(intstream_view))); assert(ranges::equal(input_value_view, expected)); +} + +istringstream some_stream{"42"}; +constexpr bool test_constexpr() { + // Default constructor is constexpr + ranges::basic_istream_view empty{}; + + // begin is constexpr??!? + (void) empty.begin(); + + // stream constructor is constexpr + ranges::basic_istream_view meow{some_stream}; + + // end is constexpr + (void) meow.end(); return true; } @@ -96,4 +114,6 @@ bool test_one_type() { int main() { test_one_type(); test_one_type(); + + static_assert(test_constexpr()); } diff --git a/tests/std/tests/P0896R4_views_istream_death/env.lst b/tests/std/tests/P0896R4_istream_view_death/env.lst similarity index 100% rename from tests/std/tests/P0896R4_views_istream_death/env.lst rename to tests/std/tests/P0896R4_istream_view_death/env.lst diff --git a/tests/std/tests/P0896R4_views_istream_death/test.cpp b/tests/std/tests/P0896R4_istream_view_death/test.cpp similarity index 88% rename from tests/std/tests/P0896R4_views_istream_death/test.cpp rename to tests/std/tests/P0896R4_istream_view_death/test.cpp index 004652c4738..82bb0a902ca 100644 --- a/tests/std/tests/P0896R4_views_istream_death/test.cpp +++ b/tests/std/tests/P0896R4_istream_view_death/test.cpp @@ -46,6 +46,13 @@ void test_dereference_no_stream() { (void) *it; } +void test_compare_no_stream() { + iview v; + auto it = v.begin(); + auto se = v.end(); + (void) (it == se); +} + void test_preincrement_end_of_stream() { istringstream stream; iview view{stream}; @@ -78,6 +85,7 @@ int main(int argc, char* argv[]) { test_preincrement_no_stream, test_postincrement_no_stream, test_dereference_no_stream, + test_compare_no_stream, test_preincrement_end_of_stream, test_postincrement_end_of_stream, test_dereference_end_of_stream, From c617e60f9a49e815b225eae0987614ba4738e93f Mon Sep 17 00:00:00 2001 From: Casey Carter Date: Mon, 12 Oct 2020 22:25:57 -0700 Subject: [PATCH 6/8] default construct death_test_executive --- tests/std/tests/P0896R4_istream_view_death/test.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/tests/std/tests/P0896R4_istream_view_death/test.cpp b/tests/std/tests/P0896R4_istream_view_death/test.cpp index 82bb0a902ca..22f76db2769 100644 --- a/tests/std/tests/P0896R4_istream_view_death/test.cpp +++ b/tests/std/tests/P0896R4_istream_view_death/test.cpp @@ -75,7 +75,7 @@ void test_dereference_end_of_stream() { } int main(int argc, char* argv[]) { - std_testing::death_test_executive exec([] {}); + std_testing::death_test_executive exec; #if _ITERATOR_DEBUG_LEVEL != 0 exec.add_death_tests({ From c550d44ff3b13848f0f7d51583af8215b507ef17 Mon Sep 17 00:00:00 2001 From: Casey Carter Date: Mon, 12 Oct 2020 22:40:06 -0700 Subject: [PATCH 7/8] Alphabetting is hard --- tests/std/test.lst | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/tests/std/test.lst b/tests/std/test.lst index 53d608a43b9..f5eb53b2b7e 100644 --- a/tests/std/test.lst +++ b/tests/std/test.lst @@ -253,6 +253,8 @@ tests\P0896R4_common_iterator tests\P0896R4_common_iterator_death tests\P0896R4_counted_iterator tests\P0896R4_counted_iterator_death +tests\P0896R4_istream_view +tests\P0896R4_istream_view_death tests\P0896R4_P1614R2_comparisons tests\P0896R4_ranges_alg_adjacent_find tests\P0896R4_ranges_alg_all_of @@ -343,8 +345,6 @@ tests\P0896R4_views_drop tests\P0896R4_views_empty tests\P0896R4_views_filter tests\P0896R4_views_filter_death -tests\P0896R4_istream_view -tests\P0896R4_istream_view_death tests\P0896R4_views_reverse tests\P0896R4_views_single tests\P0896R4_views_take From ff85a89efd770e8b683dedc3e76ac0a84ad6e72d Mon Sep 17 00:00:00 2001 From: "Stephan T. Lavavej" Date: Mon, 26 Oct 2020 20:33:39 -0700 Subject: [PATCH 8/8] Apply suggestions from code review --- stl/inc/ranges | 4 ++-- tests/std/tests/P0896R4_istream_view/test.cpp | 1 + 2 files changed, 3 insertions(+), 2 deletions(-) diff --git a/stl/inc/ranges b/stl/inc/ranges index 5b6647cc8ef..9e54f51b2cb 100644 --- a/stl/inc/ranges +++ b/stl/inc/ranges @@ -668,7 +668,7 @@ namespace ranges { _Iterator& operator++() { #if _ITERATOR_DEBUG_LEVEL != 0 // Per LWG-3489 - _STL_VERIFY(_Parent != nullptr, "cannot increment default initialized istream_view iterator"); + _STL_VERIFY(_Parent != nullptr, "cannot increment default-initialized istream_view iterator"); _STL_VERIFY( _Parent->_Stream != nullptr, "cannot increment istream_view iterator with uninitialized stream"); _STL_VERIFY(!_Parent->_Stream_at_end(), "cannot increment istream_view iterator at end of stream"); @@ -684,7 +684,7 @@ namespace ranges { _NODISCARD _Ty& operator*() const noexcept /* strengthened */ { #if _ITERATOR_DEBUG_LEVEL != 0 // Per LWG-3489 - _STL_VERIFY(_Parent != nullptr, "cannot dereference default initialized istream_view iterator"); + _STL_VERIFY(_Parent != nullptr, "cannot dereference default-initialized istream_view iterator"); _STL_VERIFY( _Parent->_Stream != nullptr, "cannot dereference istream_view iterator with uninitialized stream"); _STL_VERIFY(!_Parent->_Stream_at_end(), "cannot dereference istream_view iterator at end of stream"); diff --git a/tests/std/tests/P0896R4_istream_view/test.cpp b/tests/std/tests/P0896R4_istream_view/test.cpp index cfcd7da9f66..20d8efb5f4b 100644 --- a/tests/std/tests/P0896R4_istream_view/test.cpp +++ b/tests/std/tests/P0896R4_istream_view/test.cpp @@ -7,6 +7,7 @@ #include #include #include +#include #include