From 899f62b152bbf60b22c8cfdc0277af6a200ce71c Mon Sep 17 00:00:00 2001 From: Michael Schellenberger Costa Date: Thu, 9 Jul 2020 16:35:44 +0200 Subject: [PATCH 01/20] Implement ranges::unique family --- stl/inc/algorithm | 175 ++++++++++++++++++ tests/std/include/range_algorithm_support.hpp | 20 +- tests/std/test.lst | 2 + .../tests/P0896R4_ranges_alg_unique/env.lst | 4 + .../tests/P0896R4_ranges_alg_unique/test.cpp | 63 +++++++ .../P0896R4_ranges_alg_unique_copy/env.lst | 4 + .../P0896R4_ranges_alg_unique_copy/test.cpp | 79 ++++++++ 7 files changed, 342 insertions(+), 5 deletions(-) create mode 100644 tests/std/tests/P0896R4_ranges_alg_unique/env.lst create mode 100644 tests/std/tests/P0896R4_ranges_alg_unique/test.cpp create mode 100644 tests/std/tests/P0896R4_ranges_alg_unique_copy/env.lst create mode 100644 tests/std/tests/P0896R4_ranges_alg_unique_copy/test.cpp diff --git a/stl/inc/algorithm b/stl/inc/algorithm index 945936ee546..c2232e9c6a2 100644 --- a/stl/inc/algorithm +++ b/stl/inc/algorithm @@ -3925,6 +3925,72 @@ _NODISCARD _FwdIt unique(_ExPo&&, _FwdIt _First, _FwdIt _Last) noexcept /* termi } #endif // _HAS_CXX17 +#ifdef __cpp_lib_concepts +namespace ranges { + // VARIABLE ranges::unique + class _Unique_fn : private _Not_quite_object { + public: + using _Not_quite_object::_Not_quite_object; + + template _Se, class _Pj = identity, + indirect_equivalence_relation> _Pr = ranges::equal_to> + _NODISCARD constexpr subrange<_It> operator()(_It _First, _Se _Last, _Pr _Pred = {}, _Pj _Proj = {}) const { + _Adl_verify_range(_First, _Last); + auto _UResult = _Unique_unchecked( + _Get_unwrapped(_STD move(_First)), _Get_unwrapped(_STD move(_Last)), _Pass_fn(_Pred), _Pass_fn(_Proj)); + + return _Rewrap_subrange>(_First, _STD move(_UResult)); + } + + // clang-format off + template , _Pj>> _Pr = ranges::equal_to> + requires permutable> + _NODISCARD constexpr borrowed_subrange_t<_Rng> operator()(_Rng&& _Range, _Pr _Pred = {}, _Pj _Proj = {}) const { + auto _UResult = _Unique_unchecked(_Ubegin(_Range), _Uend(_Range), _Pass_fn(_Pred), _Pass_fn(_Proj)); + + return _Rewrap_subrange>(_Range, _STD move(_UResult)); + } + // clang-format on + private: + template + _NODISCARD static constexpr subrange<_It> _Unique_unchecked(_It _First, _Se _Last, _Pr _Pred, _Pj _Proj) { + // Remove adjacent elements from [_First, _Last) whose projection satisfy _Pred + _STL_INTERNAL_STATIC_ASSERT(permutable<_It>); + _STL_INTERNAL_STATIC_ASSERT(sentinel_for<_Se, _It>); + _STL_INTERNAL_STATIC_ASSERT(indirect_equivalence_relation<_Pr, projected<_It, _Pj>>); + + auto _Current = _First; + if (_First == _Last) { + return {_STD move(_Current), _STD move(_First)}; + } + + for (; ++_First != _Last; ++_Current) { + if (_STD invoke(_Pred, _STD invoke(_Proj, *_Current), _STD invoke(_Proj, *_First))) { + break; + } + } + + if (_First == _Last) { + return {_STD move(_Current), _STD move(_First)}; + } + + while (++_First != _Last) { + if (!_STD invoke(_Pred, _STD invoke(_Proj, *_Current), _STD invoke(_Proj, *_First))) { + ++_Current; + *_Current = _RANGES iter_move(_First); + } + } + ++_Current; + + return {_STD move(_Current), _STD move(_First)}; + } + }; + + inline constexpr _Unique_fn unique{_Not_quite_object::_Construct_tag{}}; +} // namespace ranges +#endif // __cpp_lib_concepts + // FUNCTION TEMPLATE unique_copy #if _HAS_IF_CONSTEXPR // clang-format off @@ -4096,6 +4162,115 @@ _FwdIt2 unique_copy(_ExPo&&, _FwdIt1 _First, _FwdIt1 _Last, _FwdIt2 _Dest) noexc } #endif // _HAS_CXX17 +#ifdef __cpp_lib_concepts +namespace ranges { + + // clang-format off + template + concept _Can_reread_or_store = forward_iterator<_It> + || (input_iterator<_Out> && same_as, iter_value_t<_Out>>) + || indirectly_copyable_storable<_It, _Out>; + // clang-format on + + // ALIAS TEMPLATE unique_copy_result + template + using unique_copy_result = in_out_result<_In, _Out>; + + // VARIABLE ranges::unique_copy + class _Unique_copy_fn : private _Not_quite_object { + public: + using _Not_quite_object::_Not_quite_object; + + // clang-format off + template _Se, weakly_incrementable _Out, class _Pj = identity, + indirect_equivalence_relation> _Pr = ranges::equal_to> + requires indirectly_copyable<_It, _Out> && _Can_reread_or_store<_It, _Out> + constexpr unique_copy_result<_It, _Out> operator()( + _It _First, _Se _Last, _Out _Result, _Pr _Pred = {}, _Pj _Proj = {}) const { + _Adl_verify_range(_First, _Last); + auto _UResult = _Unique_copy_unchecked(_Get_unwrapped(_STD move(_First)), _Get_unwrapped(_STD move(_Last)), + _Get_unwrapped_unverified(_STD move(_Result)), _Pass_fn(_Pred), _Pass_fn(_Proj)); + + _Seek_wrapped(_First, _STD move(_UResult.in)); + _Seek_wrapped(_Result, _STD move(_UResult.out)); + return {_STD move(_First), _STD move(_Result)}; + } + + template , _Pj>> _Pr = ranges::equal_to> + requires indirectly_copyable, _Out> && _Can_reread_or_store, _Out> + constexpr unique_copy_result, _Out> operator()( + _Rng&& _Range, _Out _Result, _Pr _Pred = {}, _Pj _Proj = {}) const { + auto _First = _RANGES begin(_Range); + auto _UResult = _Unique_copy_unchecked(_Get_unwrapped(_STD move(_First)), _Uend(_Range), + _Get_unwrapped_unverified(_STD move(_Result)), _Pass_fn(_Pred), _Pass_fn(_Proj)); + + _Seek_wrapped(_First, _STD move(_UResult.in)); + _Seek_wrapped(_Result, _STD move(_UResult.out)); + return {_STD move(_First), _STD move(_Result)}; + } + // clang-format on + private: + template + _NODISCARD static constexpr unique_copy_result<_It, _Out> _Unique_copy_unchecked( + _It _First, const _Se _Last, _Out _Result, _Pr _Pred, _Pj _Proj) { + // Copy elements from [_First, _Last) to _Out, compressing adjacent elements whose projection satisfy _Pred + _STL_INTERNAL_STATIC_ASSERT(input_iterator<_It>); + _STL_INTERNAL_STATIC_ASSERT(sentinel_for<_Se, _It>); + _STL_INTERNAL_STATIC_ASSERT(weakly_incrementable<_Out>); + _STL_INTERNAL_STATIC_ASSERT(indirectly_copyable<_It, _Out>); + _STL_INTERNAL_STATIC_ASSERT(_Can_reread_or_store<_It, _Out>); + _STL_INTERNAL_STATIC_ASSERT(indirect_equivalence_relation<_Pr, projected<_It, _Pj>>); + + if (_First == _Last) { + return {_STD move(_First), _STD move(_Result)}; + } + + if constexpr (input_iterator<_Out>) { + // Can reread _Result + *_Result = *_First; + + while (++_First != _Last) { + if (!_STD invoke(_Pred, _STD invoke(_Proj, *_Result), _STD invoke(_Proj, *_First))) { + ++_Result; + *_Result = *_First; + } + } + } else if constexpr (forward_iterator<_It>) { + // Can reread _First + auto _Current = _First; + *_Result = *_First; + + while (++_First != _Last) { + if (!_STD invoke(_Pred, _STD invoke(_Proj, *_Current), _STD invoke(_Proj, *_First))) { + _Current = _First; + ++_Result; + *_Result = *_First; + } + } + } else { + // Cannot reread _First nor _Result, construct temporary + _Iter_value_t<_It> _Val = *_First; + *_Result = _Val; + + while (++_First != _Last) { + if (!_STD invoke(_Pred, _STD invoke(_Proj, _Val), _STD invoke(_Proj, *_First))) { + _Val = *_First; + ++_Result; + *_Result = _Val; + } + } + } + ++_Result; + + return {_STD move(_First), _STD move(_Result)}; + } + }; + + inline constexpr _Unique_copy_fn unique_copy{_Not_quite_object::_Construct_tag{}}; +} // namespace ranges +#endif // __cpp_lib_concepts + // FUNCTION TEMPLATE reverse_copy template _CONSTEXPR20 _OutIt reverse_copy(_BidIt _First, _BidIt _Last, _OutIt _Dest) { diff --git a/tests/std/include/range_algorithm_support.hpp b/tests/std/include/range_algorithm_support.hpp index d5fa87ddd08..a2fbf0ebbe3 100644 --- a/tests/std/include/range_algorithm_support.hpp +++ b/tests/std/include/range_algorithm_support.hpp @@ -983,11 +983,6 @@ constexpr void test_contiguous() { with_contiguous_ranges::call(); } -template -constexpr void input_range_output_iterator_permutations() { - with_input_ranges, Element1>::call(); -} - template constexpr void test_in_in() { with_input_ranges, Element1>::call(); @@ -1003,6 +998,21 @@ constexpr void test_fwd_fwd() { with_forward_ranges, Element1>::call(); } +template +constexpr void input_range_output_iterator_permutations() { + with_input_ranges, Element1>::call(); +} + +template +constexpr void input_range_input_iterator_permutations() { + with_input_ranges, Element1>::call(); +} + +template +constexpr void forward_range_output_iterator_permutations() { + with_forward_ranges, Element1>::call(); +} + template constexpr void test_in_write() { with_input_ranges, Element1>::call(); diff --git a/tests/std/test.lst b/tests/std/test.lst index 4c9d6bf510c..cb20733cea0 100644 --- a/tests/std/test.lst +++ b/tests/std/test.lst @@ -278,6 +278,8 @@ tests\P0896R4_ranges_range_machinery tests\P0896R4_ranges_subrange tests\P0896R4_ranges_test_machinery tests\P0896R4_ranges_to_address +tests\P0896R4_ranges_unique +tests\P0896R4_ranges_unique_copy tests\P0898R3_concepts tests\P0898R3_identity tests\P0919R3_heterogeneous_unordered_lookup diff --git a/tests/std/tests/P0896R4_ranges_alg_unique/env.lst b/tests/std/tests/P0896R4_ranges_alg_unique/env.lst new file mode 100644 index 00000000000..f3ccc8613c6 --- /dev/null +++ b/tests/std/tests/P0896R4_ranges_alg_unique/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_ranges_alg_unique/test.cpp b/tests/std/tests/P0896R4_ranges_alg_unique/test.cpp new file mode 100644 index 00000000000..31b07c0c97a --- /dev/null +++ b/tests/std/tests/P0896R4_ranges_alg_unique/test.cpp @@ -0,0 +1,63 @@ +// Copyright (c) Microsoft Corporation. +// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception + +#include +#include +#include +#include +#include +#include + +#include +using namespace std; +using P = pair; + +// Validate dangling story +STATIC_ASSERT(same_as{})), ranges::dangling>); +STATIC_ASSERT(same_as{})), ranges::subrange>); + +struct instantiator { + static constexpr P expected[4] = {{0, 99}, {1, 47}, {3, 99}, {4, 47}}; + + template + static constexpr void call() { + using ranges::unique, ranges::subrange, ranges::iterator_t; + { // Validate iterator + sentinel overload + P input[6] = {{0, 99}, {1, 47}, {2, 47}, {3, 99}, {4, 47}, {5, 47}}; + ReadWrite wrapped_input{input}; + size_t comparisonsCounter = 0; + constexpr auto countedEq = [&comparisonsCounter](const int a, const int b) { + ++comparisonsCounter; + return a == b; + }; + + auto result = unique(wrapped_input.begin(), wrapped_input.end(), countedEq, get_second); + STATIC_ASSERT(same_as>>); + assert(result.begin() == next(wrapped_input.begin(), 4)); + assert(result.end() == wrapped_input.end()); + assert(ranges::equal(expected, span{input}.first<4>())); + assert(comparisonsCounter == ranges::size(input) - 1); + } + { // Validate range overload + P input[6] = {{0, 99}, {1, 47}, {2, 47}, {3, 99}, {4, 47}, {5, 47}}; + ReadWrite wrapped_input{input}; + size_t comparisonsCounter = 0; + constexpr auto countedEq = [&comparisonsCounter](const int a, const int b) { + ++comparisonsCounter; + return a == b; + }; + + auto result = unique(wrapped_input, countedEq, get_second); + STATIC_ASSERT(same_as>>); + assert(result.begin() == next(wrapped_input.begin(), 4)); + assert(result.end() == wrapped_input.end()); + assert(ranges::equal(expected, span{input}.first<4>())); + assert(comparisonsCounter == ranges::size(input) - 1); + } + } +}; + +int main() { + STATIC_ASSERT((test_fwd(), true)); + test_fwd(); +} diff --git a/tests/std/tests/P0896R4_ranges_alg_unique_copy/env.lst b/tests/std/tests/P0896R4_ranges_alg_unique_copy/env.lst new file mode 100644 index 00000000000..f3ccc8613c6 --- /dev/null +++ b/tests/std/tests/P0896R4_ranges_alg_unique_copy/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_ranges_alg_unique_copy/test.cpp b/tests/std/tests/P0896R4_ranges_alg_unique_copy/test.cpp new file mode 100644 index 00000000000..7211ca87d0c --- /dev/null +++ b/tests/std/tests/P0896R4_ranges_alg_unique_copy/test.cpp @@ -0,0 +1,79 @@ +// Copyright (c) Microsoft Corporation. +// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception + +#include +#include +#include +#include +#include +#include + +#include +using namespace std; +using P = pair; + +// Validate that unique_copy aliases in_out_result +STATIC_ASSERT(same_as, ranges::in_out_result>); + +// Validate dangling story +STATIC_ASSERT(same_as{}, static_cast(nullptr))), + ranges::unique_copy_result>); +STATIC_ASSERT(same_as{}, static_cast(nullptr))), + ranges::unique_copy_result>); + +struct instantiator { + static constexpr P expected[4] = {{0, 99}, {1, 47}, {3, 99}, {4, 47}}; + static constexpr P input[6] = {{0, 99}, {1, 47}, {2, 47}, {3, 99}, {4, 47}, {5, 47}}; + + template + static constexpr void call() { + using ranges::unique_copy, ranges::unique_copy_result, ranges::iterator_t; + { // Validate iterator + sentinel overload + P output[4] = {{-1, -1}, {-1, -1}, {-1, -1}, {-1, -1}}; + Read wrapped_input{input}; + size_t equalCounter = 0; + constexpr auto countedEqual = [&](const int a, const int b) { + ++equalCounter; + return a == b; + }; + + auto result = + unique_copy(wrapped_input.begin(), wrapped_input.end(), Write{output}, countedEqual, get_second); + STATIC_ASSERT(same_as, Write>>); + assert(result.in == wrapped_input.end()); + assert(result.out.peek() == output + 4); + assert(ranges::equal(expected, output)); + assert(equalCounter == 5); + } + { // Validate range overload + P output[4] = {{-1, -1}, {-1, -1}, {-1, -1}, {-1, -1}}; + Read wrapped_input{input}; + size_t equalCounter = 0; + constexpr auto countedEqual = [&](const int a, const int b) { + ++equalCounter; + return a == b; + }; + + auto result = unique_copy(wrapped_input, Write{output}, countedEqual, get_second); + STATIC_ASSERT(same_as, Write>>); + assert(result.in == wrapped_input.end()); + assert(result.out.peek() == output + 4); + assert(ranges::equal(expected, output)); + assert(equalCounter == 5); + } + } +}; + +int main() { + // with store + STATIC_ASSERT((input_range_output_iterator_permutations(), true)); + input_range_output_iterator_permutations(); + + // with readable out + STATIC_ASSERT((input_range_input_iterator_permutations(), true)); + input_range_input_iterator_permutations(); + + // with readable in + STATIC_ASSERT((forward_range_output_iterator_permutations(), true)); + forward_range_output_iterator_permutations(); +} From 6f200dfd3b7ece09d9104ef1696eb602d580d78b Mon Sep 17 00:00:00 2001 From: Michael Schellenberger Costa Date: Mon, 13 Jul 2020 08:39:31 +0200 Subject: [PATCH 02/20] Apply suggestions from STL Co-authored-by: Stephan T. Lavavej --- stl/inc/algorithm | 8 ++++---- tests/std/test.lst | 4 ++-- tests/std/tests/P0896R4_ranges_alg_unique_copy/test.cpp | 2 +- 3 files changed, 7 insertions(+), 7 deletions(-) diff --git a/stl/inc/algorithm b/stl/inc/algorithm index c2232e9c6a2..69a30c1c008 100644 --- a/stl/inc/algorithm +++ b/stl/inc/algorithm @@ -3955,7 +3955,7 @@ namespace ranges { private: template _NODISCARD static constexpr subrange<_It> _Unique_unchecked(_It _First, _Se _Last, _Pr _Pred, _Pj _Proj) { - // Remove adjacent elements from [_First, _Last) whose projection satisfy _Pred + // Remove adjacent elements from [_First, _Last) whose projections satisfy _Pred _STL_INTERNAL_STATIC_ASSERT(permutable<_It>); _STL_INTERNAL_STATIC_ASSERT(sentinel_for<_Se, _It>); _STL_INTERNAL_STATIC_ASSERT(indirect_equivalence_relation<_Pr, projected<_It, _Pj>>); @@ -4196,7 +4196,7 @@ namespace ranges { return {_STD move(_First), _STD move(_Result)}; } - template , _Pj>> _Pr = ranges::equal_to> requires indirectly_copyable, _Out> && _Can_reread_or_store, _Out> constexpr unique_copy_result, _Out> operator()( @@ -4214,7 +4214,7 @@ namespace ranges { template _NODISCARD static constexpr unique_copy_result<_It, _Out> _Unique_copy_unchecked( _It _First, const _Se _Last, _Out _Result, _Pr _Pred, _Pj _Proj) { - // Copy elements from [_First, _Last) to _Out, compressing adjacent elements whose projection satisfy _Pred + // Copy elements from [_First, _Last) to _Result, compressing adjacent elements whose projections satisfy _Pred _STL_INTERNAL_STATIC_ASSERT(input_iterator<_It>); _STL_INTERNAL_STATIC_ASSERT(sentinel_for<_Se, _It>); _STL_INTERNAL_STATIC_ASSERT(weakly_incrementable<_Out>); @@ -4249,7 +4249,7 @@ namespace ranges { } } } else { - // Cannot reread _First nor _Result, construct temporary + // Neither _First nor _Result can be reread, construct temporary _Iter_value_t<_It> _Val = *_First; *_Result = _Val; diff --git a/tests/std/test.lst b/tests/std/test.lst index cb20733cea0..4952e83737a 100644 --- a/tests/std/test.lst +++ b/tests/std/test.lst @@ -278,8 +278,8 @@ tests\P0896R4_ranges_range_machinery tests\P0896R4_ranges_subrange tests\P0896R4_ranges_test_machinery tests\P0896R4_ranges_to_address -tests\P0896R4_ranges_unique -tests\P0896R4_ranges_unique_copy +tests\P0896R4_ranges_alg_unique +tests\P0896R4_ranges_alg_unique_copy tests\P0898R3_concepts tests\P0898R3_identity tests\P0919R3_heterogeneous_unordered_lookup diff --git a/tests/std/tests/P0896R4_ranges_alg_unique_copy/test.cpp b/tests/std/tests/P0896R4_ranges_alg_unique_copy/test.cpp index 7211ca87d0c..8be16af60ca 100644 --- a/tests/std/tests/P0896R4_ranges_alg_unique_copy/test.cpp +++ b/tests/std/tests/P0896R4_ranges_alg_unique_copy/test.cpp @@ -12,7 +12,7 @@ using namespace std; using P = pair; -// Validate that unique_copy aliases in_out_result +// Validate that unique_copy_result aliases in_out_result STATIC_ASSERT(same_as, ranges::in_out_result>); // Validate dangling story From 17bbb37d4d58b0aeb515226029737ee686ec4ed5 Mon Sep 17 00:00:00 2001 From: Michael Schellenberger Costa Date: Mon, 13 Jul 2020 08:41:54 +0200 Subject: [PATCH 03/20] More review comments and formatting --- stl/inc/algorithm | 3 +- .../P0896R4_ranges_alg_unique_copy/test.cpp | 29 +++++++++---------- 2 files changed, 16 insertions(+), 16 deletions(-) diff --git a/stl/inc/algorithm b/stl/inc/algorithm index 69a30c1c008..413b30c90ca 100644 --- a/stl/inc/algorithm +++ b/stl/inc/algorithm @@ -4214,7 +4214,8 @@ namespace ranges { template _NODISCARD static constexpr unique_copy_result<_It, _Out> _Unique_copy_unchecked( _It _First, const _Se _Last, _Out _Result, _Pr _Pred, _Pj _Proj) { - // Copy elements from [_First, _Last) to _Result, compressing adjacent elements whose projections satisfy _Pred + // Copy elements from [_First, _Last) to _Result, compressing adjacent elements whose projections satisfy + // _Pred _STL_INTERNAL_STATIC_ASSERT(input_iterator<_It>); _STL_INTERNAL_STATIC_ASSERT(sentinel_for<_Se, _It>); _STL_INTERNAL_STATIC_ASSERT(weakly_incrementable<_Out>); diff --git a/tests/std/tests/P0896R4_ranges_alg_unique_copy/test.cpp b/tests/std/tests/P0896R4_ranges_alg_unique_copy/test.cpp index 8be16af60ca..2d9cb1b035f 100644 --- a/tests/std/tests/P0896R4_ranges_alg_unique_copy/test.cpp +++ b/tests/std/tests/P0896R4_ranges_alg_unique_copy/test.cpp @@ -16,26 +16,27 @@ using P = pair; STATIC_ASSERT(same_as, ranges::in_out_result>); // Validate dangling story -STATIC_ASSERT(same_as{}, static_cast(nullptr))), +STATIC_ASSERT(same_as{}, nullptr_to)), ranges::unique_copy_result>); -STATIC_ASSERT(same_as{}, static_cast(nullptr))), - ranges::unique_copy_result>); +STATIC_ASSERT( + same_as{}, nullptr_to)), ranges::unique_copy_result>); struct instantiator { static constexpr P expected[4] = {{0, 99}, {1, 47}, {3, 99}, {4, 47}}; static constexpr P input[6] = {{0, 99}, {1, 47}, {2, 47}, {3, 99}, {4, 47}, {5, 47}}; + size_t comparisonCounter = 0; + constexpr auto countedEqual = [&](const int a, const int b) { + ++comparisonCounter; + return a == b; + }; + template static constexpr void call() { using ranges::unique_copy, ranges::unique_copy_result, ranges::iterator_t; { // Validate iterator + sentinel overload P output[4] = {{-1, -1}, {-1, -1}, {-1, -1}, {-1, -1}}; Read wrapped_input{input}; - size_t equalCounter = 0; - constexpr auto countedEqual = [&](const int a, const int b) { - ++equalCounter; - return a == b; - }; auto result = unique_copy(wrapped_input.begin(), wrapped_input.end(), Write{output}, countedEqual, get_second); @@ -43,23 +44,21 @@ struct instantiator { assert(result.in == wrapped_input.end()); assert(result.out.peek() == output + 4); assert(ranges::equal(expected, output)); - assert(equalCounter == 5); + assert(comparisonCounter == 5); } + + comparisonCounter = 0; + { // Validate range overload P output[4] = {{-1, -1}, {-1, -1}, {-1, -1}, {-1, -1}}; Read wrapped_input{input}; - size_t equalCounter = 0; - constexpr auto countedEqual = [&](const int a, const int b) { - ++equalCounter; - return a == b; - }; auto result = unique_copy(wrapped_input, Write{output}, countedEqual, get_second); STATIC_ASSERT(same_as, Write>>); assert(result.in == wrapped_input.end()); assert(result.out.peek() == output + 4); assert(ranges::equal(expected, output)); - assert(equalCounter == 5); + assert(comparisonCounter == 5); } } }; From 22e019b29e58f592ef41d03c53d326d0fc77cdb3 Mon Sep 17 00:00:00 2001 From: Michael Schellenberger Costa Date: Mon, 13 Jul 2020 13:53:40 +0200 Subject: [PATCH 04/20] Pull the instrumented comparator into the call function --- .../tests/P0896R4_ranges_alg_unique/test.cpp | 24 +++++++++---------- .../P0896R4_ranges_alg_unique_copy/test.cpp | 18 +++++++------- 2 files changed, 21 insertions(+), 21 deletions(-) diff --git a/tests/std/tests/P0896R4_ranges_alg_unique/test.cpp b/tests/std/tests/P0896R4_ranges_alg_unique/test.cpp index 31b07c0c97a..1798eea0ec4 100644 --- a/tests/std/tests/P0896R4_ranges_alg_unique/test.cpp +++ b/tests/std/tests/P0896R4_ranges_alg_unique/test.cpp @@ -22,37 +22,37 @@ struct instantiator { template static constexpr void call() { using ranges::unique, ranges::subrange, ranges::iterator_t; + + size_t comparisonCounter = 0; + auto countedEq = [&comparisonCounter](const int a, const int b) { + ++comparisonCounter; + return a == b; + }; + { // Validate iterator + sentinel overload P input[6] = {{0, 99}, {1, 47}, {2, 47}, {3, 99}, {4, 47}, {5, 47}}; ReadWrite wrapped_input{input}; - size_t comparisonsCounter = 0; - constexpr auto countedEq = [&comparisonsCounter](const int a, const int b) { - ++comparisonsCounter; - return a == b; - }; auto result = unique(wrapped_input.begin(), wrapped_input.end(), countedEq, get_second); STATIC_ASSERT(same_as>>); assert(result.begin() == next(wrapped_input.begin(), 4)); assert(result.end() == wrapped_input.end()); assert(ranges::equal(expected, span{input}.first<4>())); - assert(comparisonsCounter == ranges::size(input) - 1); + assert(comparisonCounter == ranges::size(input) - 1); } + + comparisonCounter = 0; + { // Validate range overload P input[6] = {{0, 99}, {1, 47}, {2, 47}, {3, 99}, {4, 47}, {5, 47}}; ReadWrite wrapped_input{input}; - size_t comparisonsCounter = 0; - constexpr auto countedEq = [&comparisonsCounter](const int a, const int b) { - ++comparisonsCounter; - return a == b; - }; auto result = unique(wrapped_input, countedEq, get_second); STATIC_ASSERT(same_as>>); assert(result.begin() == next(wrapped_input.begin(), 4)); assert(result.end() == wrapped_input.end()); assert(ranges::equal(expected, span{input}.first<4>())); - assert(comparisonsCounter == ranges::size(input) - 1); + assert(comparisonCounter == ranges::size(input) - 1); } } }; diff --git a/tests/std/tests/P0896R4_ranges_alg_unique_copy/test.cpp b/tests/std/tests/P0896R4_ranges_alg_unique_copy/test.cpp index 2d9cb1b035f..841ec96c194 100644 --- a/tests/std/tests/P0896R4_ranges_alg_unique_copy/test.cpp +++ b/tests/std/tests/P0896R4_ranges_alg_unique_copy/test.cpp @@ -25,21 +25,21 @@ struct instantiator { static constexpr P expected[4] = {{0, 99}, {1, 47}, {3, 99}, {4, 47}}; static constexpr P input[6] = {{0, 99}, {1, 47}, {2, 47}, {3, 99}, {4, 47}, {5, 47}}; - size_t comparisonCounter = 0; - constexpr auto countedEqual = [&](const int a, const int b) { - ++comparisonCounter; - return a == b; - }; - template static constexpr void call() { using ranges::unique_copy, ranges::unique_copy_result, ranges::iterator_t; + + size_t comparisonCounter = 0; + auto countedEq = [&comparisonCounter](const int a, const int b) { + ++comparisonCounter; + return a == b; + }; + { // Validate iterator + sentinel overload P output[4] = {{-1, -1}, {-1, -1}, {-1, -1}, {-1, -1}}; Read wrapped_input{input}; - auto result = - unique_copy(wrapped_input.begin(), wrapped_input.end(), Write{output}, countedEqual, get_second); + auto result = unique_copy(wrapped_input.begin(), wrapped_input.end(), Write{output}, countedEq, get_second); STATIC_ASSERT(same_as, Write>>); assert(result.in == wrapped_input.end()); assert(result.out.peek() == output + 4); @@ -53,7 +53,7 @@ struct instantiator { P output[4] = {{-1, -1}, {-1, -1}, {-1, -1}, {-1, -1}}; Read wrapped_input{input}; - auto result = unique_copy(wrapped_input, Write{output}, countedEqual, get_second); + auto result = unique_copy(wrapped_input, Write{output}, countedEq, get_second); STATIC_ASSERT(same_as, Write>>); assert(result.in == wrapped_input.end()); assert(result.out.peek() == output + 4); From 2dd956749bcd6417de4ff07e9675138b03098e86 Mon Sep 17 00:00:00 2001 From: Michael Schellenberger Costa Date: Mon, 13 Jul 2020 18:45:44 +0200 Subject: [PATCH 05/20] Apply suggestions from Casey Co-authored-by: Casey Carter --- stl/inc/algorithm | 26 +++++++++---------- .../tests/P0896R4_ranges_alg_unique/test.cpp | 4 +-- 2 files changed, 14 insertions(+), 16 deletions(-) diff --git a/stl/inc/algorithm b/stl/inc/algorithm index 413b30c90ca..c6be77eb002 100644 --- a/stl/inc/algorithm +++ b/stl/inc/algorithm @@ -3954,7 +3954,7 @@ namespace ranges { // clang-format on private: template - _NODISCARD static constexpr subrange<_It> _Unique_unchecked(_It _First, _Se _Last, _Pr _Pred, _Pj _Proj) { + _NODISCARD static constexpr subrange<_It> _Unique_unchecked(_It _First, const _Se _Last, _Pr _Pred, _Pj _Proj) { // Remove adjacent elements from [_First, _Last) whose projections satisfy _Pred _STL_INTERNAL_STATIC_ASSERT(permutable<_It>); _STL_INTERNAL_STATIC_ASSERT(sentinel_for<_Se, _It>); @@ -3965,16 +3965,16 @@ namespace ranges { return {_STD move(_Current), _STD move(_First)}; } - for (; ++_First != _Last; ++_Current) { + for (;; ++_Current) { + if (++_First == _Last) { + return {_STD move(_Current), _STD move(_First)}; + } + if (_STD invoke(_Pred, _STD invoke(_Proj, *_Current), _STD invoke(_Proj, *_First))) { break; } } - if (_First == _Last) { - return {_STD move(_Current), _STD move(_First)}; - } - while (++_First != _Last) { if (!_STD invoke(_Pred, _STD invoke(_Proj, *_Current), _STD invoke(_Proj, *_First))) { ++_Current; @@ -4164,19 +4164,17 @@ _FwdIt2 unique_copy(_ExPo&&, _FwdIt1 _First, _FwdIt1 _Last, _FwdIt2 _Dest) noexc #ifdef __cpp_lib_concepts namespace ranges { + // ALIAS TEMPLATE unique_copy_result + template + using unique_copy_result = in_out_result<_In, _Out>; + // VARIABLE ranges::unique_copy // clang-format off template concept _Can_reread_or_store = forward_iterator<_It> || (input_iterator<_Out> && same_as, iter_value_t<_Out>>) || indirectly_copyable_storable<_It, _Out>; // clang-format on - - // ALIAS TEMPLATE unique_copy_result - template - using unique_copy_result = in_out_result<_In, _Out>; - - // VARIABLE ranges::unique_copy class _Unique_copy_fn : private _Not_quite_object { public: using _Not_quite_object::_Not_quite_object; @@ -4227,7 +4225,7 @@ namespace ranges { return {_STD move(_First), _STD move(_Result)}; } - if constexpr (input_iterator<_Out>) { + if constexpr (input_iterator<_Out> && same_as, iter_value_t<_Out>>) { // Can reread _Result *_Result = *_First; @@ -4251,7 +4249,7 @@ namespace ranges { } } else { // Neither _First nor _Result can be reread, construct temporary - _Iter_value_t<_It> _Val = *_First; + iter_value_t<_It> _Val = *_First; *_Result = _Val; while (++_First != _Last) { diff --git a/tests/std/tests/P0896R4_ranges_alg_unique/test.cpp b/tests/std/tests/P0896R4_ranges_alg_unique/test.cpp index 1798eea0ec4..19a2f4b9985 100644 --- a/tests/std/tests/P0896R4_ranges_alg_unique/test.cpp +++ b/tests/std/tests/P0896R4_ranges_alg_unique/test.cpp @@ -37,7 +37,7 @@ struct instantiator { STATIC_ASSERT(same_as>>); assert(result.begin() == next(wrapped_input.begin(), 4)); assert(result.end() == wrapped_input.end()); - assert(ranges::equal(expected, span{input}.first<4>())); + assert(ranges::equal(expected, result)); assert(comparisonCounter == ranges::size(input) - 1); } @@ -51,7 +51,7 @@ struct instantiator { STATIC_ASSERT(same_as>>); assert(result.begin() == next(wrapped_input.begin(), 4)); assert(result.end() == wrapped_input.end()); - assert(ranges::equal(expected, span{input}.first<4>())); + assert(ranges::equal(expected, result)); assert(comparisonCounter == ranges::size(input) - 1); } } From 232cf70beabba57903520b774e0e23876f340c31 Mon Sep 17 00:00:00 2001 From: Michael Schellenberger Costa Date: Mon, 13 Jul 2020 20:35:55 +0200 Subject: [PATCH 06/20] Only use necessary instantiator --- stl/inc/algorithm | 6 +++--- tests/std/include/range_algorithm_support.hpp | 10 ---------- .../tests/P0896R4_ranges_alg_unique_copy/test.cpp | 13 ++----------- 3 files changed, 5 insertions(+), 24 deletions(-) diff --git a/stl/inc/algorithm b/stl/inc/algorithm index c6be77eb002..5a3464edcb1 100644 --- a/stl/inc/algorithm +++ b/stl/inc/algorithm @@ -4250,15 +4250,15 @@ namespace ranges { } else { // Neither _First nor _Result can be reread, construct temporary iter_value_t<_It> _Val = *_First; - *_Result = _Val; while (++_First != _Last) { if (!_STD invoke(_Pred, _STD invoke(_Proj, _Val), _STD invoke(_Proj, *_First))) { - _Val = *_First; + *_Result = _STD move(_Val); ++_Result; - *_Result = _Val; + _Val = *_First; } } + *_Result = _STD move(_Val); } ++_Result; diff --git a/tests/std/include/range_algorithm_support.hpp b/tests/std/include/range_algorithm_support.hpp index a2fbf0ebbe3..6755503474e 100644 --- a/tests/std/include/range_algorithm_support.hpp +++ b/tests/std/include/range_algorithm_support.hpp @@ -1003,16 +1003,6 @@ constexpr void input_range_output_iterator_permutations() { with_input_ranges, Element1>::call(); } -template -constexpr void input_range_input_iterator_permutations() { - with_input_ranges, Element1>::call(); -} - -template -constexpr void forward_range_output_iterator_permutations() { - with_forward_ranges, Element1>::call(); -} - template constexpr void test_in_write() { with_input_ranges, Element1>::call(); diff --git a/tests/std/tests/P0896R4_ranges_alg_unique_copy/test.cpp b/tests/std/tests/P0896R4_ranges_alg_unique_copy/test.cpp index 841ec96c194..fddbcce3f0c 100644 --- a/tests/std/tests/P0896R4_ranges_alg_unique_copy/test.cpp +++ b/tests/std/tests/P0896R4_ranges_alg_unique_copy/test.cpp @@ -64,15 +64,6 @@ struct instantiator { }; int main() { - // with store - STATIC_ASSERT((input_range_output_iterator_permutations(), true)); - input_range_output_iterator_permutations(); - - // with readable out - STATIC_ASSERT((input_range_input_iterator_permutations(), true)); - input_range_input_iterator_permutations(); - - // with readable in - STATIC_ASSERT((forward_range_output_iterator_permutations(), true)); - forward_range_output_iterator_permutations(); + STATIC_ASSERT((test_in_write(), true)); + test_in_write(); } From 7d86eda52c02719a65690c8af4d4e51f4aea2015 Mon Sep 17 00:00:00 2001 From: Michael Schellenberger Costa Date: Mon, 13 Jul 2020 21:06:02 +0200 Subject: [PATCH 07/20] Add test that we use the correct implementation when applicable --- .../P0896R4_ranges_alg_unique_copy/test.cpp | 77 +++++++++++++++---- 1 file changed, 62 insertions(+), 15 deletions(-) diff --git a/tests/std/tests/P0896R4_ranges_alg_unique_copy/test.cpp b/tests/std/tests/P0896R4_ranges_alg_unique_copy/test.cpp index fddbcce3f0c..0dfd2396ccb 100644 --- a/tests/std/tests/P0896R4_ranges_alg_unique_copy/test.cpp +++ b/tests/std/tests/P0896R4_ranges_alg_unique_copy/test.cpp @@ -27,38 +27,85 @@ struct instantiator { template static constexpr void call() { - using ranges::unique_copy, ranges::unique_copy_result, ranges::iterator_t; - - size_t comparisonCounter = 0; - auto countedEq = [&comparisonCounter](const int a, const int b) { - ++comparisonCounter; - return a == b; - }; + using ranges::unique_copy, ranges::unique_copy_result, ranges::equal, ranges::equal_to, ranges::iterator_t; { // Validate iterator + sentinel overload P output[4] = {{-1, -1}, {-1, -1}, {-1, -1}, {-1, -1}}; Read wrapped_input{input}; - auto result = unique_copy(wrapped_input.begin(), wrapped_input.end(), Write{output}, countedEq, get_second); + // Tests which implementation strategy was choosen + size_t inputCounter = 0; + size_t outputCounter = 0; + size_t storeCounter = 0; + auto countedProjection = [&](const P& val) { + if (output <= addressof(val) && addressof(val) < output + 4) { + ++outputCounter; + } else if (input <= addressof(val) && addressof(val) < input + 6) { + ++inputCounter; + } else { + ++storeCounter; + } + return val.second; + }; + + auto result = + unique_copy(wrapped_input.begin(), wrapped_input.end(), Write{output}, equal_to{}, countedProjection); STATIC_ASSERT(same_as, Write>>); assert(result.in == wrapped_input.end()); assert(result.out.peek() == output + 4); - assert(ranges::equal(expected, output)); - assert(comparisonCounter == 5); + assert(equal(expected, output)); + if constexpr (input_iterator) { + assert(inputCounter == 5); + assert(outputCounter == 5); + assert(storeCounter == 0); + } else if constexpr (ranges::forward_range) { + assert(inputCounter == 10); + assert(outputCounter == 0); + assert(storeCounter == 0); + } else { + assert(inputCounter == 5); + assert(outputCounter == 0); + assert(storeCounter == 5); + } } - comparisonCounter = 0; - { // Validate range overload P output[4] = {{-1, -1}, {-1, -1}, {-1, -1}, {-1, -1}}; Read wrapped_input{input}; - auto result = unique_copy(wrapped_input, Write{output}, countedEq, get_second); + // Tests which implementation strategy was choosen + size_t inputCounter = 0; + size_t outputCounter = 0; + size_t storeCounter = 0; + auto countedProjection = [&](const P& val) { + if (output <= addressof(val) && addressof(val) < output + 4) { + ++outputCounter; + } else if (input <= addressof(val) && addressof(val) < input + 6) { + ++inputCounter; + } else { + ++storeCounter; + } + return val.second; + }; + + auto result = unique_copy(wrapped_input, Write{output}, equal_to{}, countedProjection); STATIC_ASSERT(same_as, Write>>); assert(result.in == wrapped_input.end()); assert(result.out.peek() == output + 4); - assert(ranges::equal(expected, output)); - assert(comparisonCounter == 5); + assert(equal(expected, output)); + if constexpr (input_iterator) { + assert(inputCounter == 5); + assert(outputCounter == 5); + assert(storeCounter == 0); + } else if constexpr (ranges::forward_range) { + assert(inputCounter == 10); + assert(outputCounter == 0); + assert(storeCounter == 0); + } else { + assert(inputCounter == 5); + assert(outputCounter == 0); + assert(storeCounter == 5); + } } } }; From 704913e3e23f73d990eac65d5777cf0a76fd0c0d Mon Sep 17 00:00:00 2001 From: Michael Schellenberger Costa Date: Mon, 13 Jul 2020 23:09:39 +0200 Subject: [PATCH 08/20] Check the correct range --- tests/std/tests/P0896R4_ranges_alg_unique/test.cpp | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/tests/std/tests/P0896R4_ranges_alg_unique/test.cpp b/tests/std/tests/P0896R4_ranges_alg_unique/test.cpp index 19a2f4b9985..1798eea0ec4 100644 --- a/tests/std/tests/P0896R4_ranges_alg_unique/test.cpp +++ b/tests/std/tests/P0896R4_ranges_alg_unique/test.cpp @@ -37,7 +37,7 @@ struct instantiator { STATIC_ASSERT(same_as>>); assert(result.begin() == next(wrapped_input.begin(), 4)); assert(result.end() == wrapped_input.end()); - assert(ranges::equal(expected, result)); + assert(ranges::equal(expected, span{input}.first<4>())); assert(comparisonCounter == ranges::size(input) - 1); } @@ -51,7 +51,7 @@ struct instantiator { STATIC_ASSERT(same_as>>); assert(result.begin() == next(wrapped_input.begin(), 4)); assert(result.end() == wrapped_input.end()); - assert(ranges::equal(expected, result)); + assert(ranges::equal(expected, span{input}.first<4>())); assert(comparisonCounter == ranges::size(input) - 1); } } From ca1e0525088bddc03f55f95a57c6f357009e2be7 Mon Sep 17 00:00:00 2001 From: Michael Schellenberger Costa Date: Tue, 14 Jul 2020 09:27:20 +0200 Subject: [PATCH 09/20] Apply suggestions from code review Co-authored-by: Stephan T. Lavavej --- .../P0896R4_ranges_alg_unique_copy/test.cpp | 32 +++++++++---------- 1 file changed, 16 insertions(+), 16 deletions(-) diff --git a/tests/std/tests/P0896R4_ranges_alg_unique_copy/test.cpp b/tests/std/tests/P0896R4_ranges_alg_unique_copy/test.cpp index 0dfd2396ccb..dd4b68bbe66 100644 --- a/tests/std/tests/P0896R4_ranges_alg_unique_copy/test.cpp +++ b/tests/std/tests/P0896R4_ranges_alg_unique_copy/test.cpp @@ -33,14 +33,14 @@ struct instantiator { P output[4] = {{-1, -1}, {-1, -1}, {-1, -1}, {-1, -1}}; Read wrapped_input{input}; - // Tests which implementation strategy was choosen + // Tests which implementation strategy was chosen size_t inputCounter = 0; size_t outputCounter = 0; size_t storeCounter = 0; auto countedProjection = [&](const P& val) { - if (output <= addressof(val) && addressof(val) < output + 4) { + if (output <= addressof(val) && addressof(val) < end(output)) { ++outputCounter; - } else if (input <= addressof(val) && addressof(val) < input + 6) { + } else if (input <= addressof(val) && addressof(val) < end(input)) { ++inputCounter; } else { ++storeCounter; @@ -55,17 +55,17 @@ struct instantiator { assert(result.out.peek() == output + 4); assert(equal(expected, output)); if constexpr (input_iterator) { - assert(inputCounter == 5); - assert(outputCounter == 5); + assert(inputCounter == ranges::size(input) - 1); + assert(outputCounter == ranges::size(input) - 1); assert(storeCounter == 0); } else if constexpr (ranges::forward_range) { - assert(inputCounter == 10); + assert(inputCounter == 2 * (ranges::size(input) - 1)); assert(outputCounter == 0); assert(storeCounter == 0); } else { - assert(inputCounter == 5); + assert(inputCounter == ranges::size(input) - 1); assert(outputCounter == 0); - assert(storeCounter == 5); + assert(storeCounter == ranges::size(input) - 1); } } @@ -73,14 +73,14 @@ struct instantiator { P output[4] = {{-1, -1}, {-1, -1}, {-1, -1}, {-1, -1}}; Read wrapped_input{input}; - // Tests which implementation strategy was choosen + // Tests which implementation strategy was chosen size_t inputCounter = 0; size_t outputCounter = 0; size_t storeCounter = 0; auto countedProjection = [&](const P& val) { - if (output <= addressof(val) && addressof(val) < output + 4) { + if (output <= addressof(val) && addressof(val) < end(output)) { ++outputCounter; - } else if (input <= addressof(val) && addressof(val) < input + 6) { + } else if (input <= addressof(val) && addressof(val) < end(input)) { ++inputCounter; } else { ++storeCounter; @@ -94,17 +94,17 @@ struct instantiator { assert(result.out.peek() == output + 4); assert(equal(expected, output)); if constexpr (input_iterator) { - assert(inputCounter == 5); - assert(outputCounter == 5); + assert(inputCounter == ranges::size(input) - 1); + assert(outputCounter == ranges::size(input) - 1); assert(storeCounter == 0); } else if constexpr (ranges::forward_range) { - assert(inputCounter == 10); + assert(inputCounter == 2 * (ranges::size(input) - 1)); assert(outputCounter == 0); assert(storeCounter == 0); } else { - assert(inputCounter == 5); + assert(inputCounter == ranges::size(input) - 1); assert(outputCounter == 0); - assert(storeCounter == 5); + assert(storeCounter == ranges::size(input) - 1); } } } From b4fc883bc8d4d2f8872dbd57866d58f485cb92de Mon Sep 17 00:00:00 2001 From: Michael Schellenberger Costa Date: Tue, 14 Jul 2020 09:28:42 +0200 Subject: [PATCH 10/20] Fix ordering of tests --- 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 4952e83737a..ef165b0ff64 100644 --- a/tests/std/test.lst +++ b/tests/std/test.lst @@ -273,13 +273,13 @@ tests\P0896R4_ranges_alg_replace_if tests\P0896R4_ranges_alg_search tests\P0896R4_ranges_alg_search_n tests\P0896R4_ranges_alg_swap_ranges +tests\P0896R4_ranges_alg_unique +tests\P0896R4_ranges_alg_unique_copy tests\P0896R4_ranges_iterator_machinery tests\P0896R4_ranges_range_machinery tests\P0896R4_ranges_subrange tests\P0896R4_ranges_test_machinery tests\P0896R4_ranges_to_address -tests\P0896R4_ranges_alg_unique -tests\P0896R4_ranges_alg_unique_copy tests\P0898R3_concepts tests\P0898R3_identity tests\P0919R3_heterogeneous_unordered_lookup From f4eba0b0d11fafdd680a938e3ab25697b15ca3c5 Mon Sep 17 00:00:00 2001 From: Michael Schellenberger Costa Date: Tue, 14 Jul 2020 10:07:03 +0200 Subject: [PATCH 11/20] Pull in ranges::equal and ranges::size so that CI restarts --- .../tests/P0896R4_ranges_alg_unique/test.cpp | 10 ++++---- .../P0896R4_ranges_alg_unique_copy/test.cpp | 23 ++++++++++--------- 2 files changed, 17 insertions(+), 16 deletions(-) diff --git a/tests/std/tests/P0896R4_ranges_alg_unique/test.cpp b/tests/std/tests/P0896R4_ranges_alg_unique/test.cpp index 1798eea0ec4..63cb6bff22c 100644 --- a/tests/std/tests/P0896R4_ranges_alg_unique/test.cpp +++ b/tests/std/tests/P0896R4_ranges_alg_unique/test.cpp @@ -21,7 +21,7 @@ struct instantiator { template static constexpr void call() { - using ranges::unique, ranges::subrange, ranges::iterator_t; + using ranges::unique, ranges::subrange, ranges::equal, ranges::size, ranges::iterator_t; size_t comparisonCounter = 0; auto countedEq = [&comparisonCounter](const int a, const int b) { @@ -37,8 +37,8 @@ struct instantiator { STATIC_ASSERT(same_as>>); assert(result.begin() == next(wrapped_input.begin(), 4)); assert(result.end() == wrapped_input.end()); - assert(ranges::equal(expected, span{input}.first<4>())); - assert(comparisonCounter == ranges::size(input) - 1); + assert(equal(expected, span{input}.first<4>())); + assert(comparisonCounter == size(input) - 1); } comparisonCounter = 0; @@ -51,8 +51,8 @@ struct instantiator { STATIC_ASSERT(same_as>>); assert(result.begin() == next(wrapped_input.begin(), 4)); assert(result.end() == wrapped_input.end()); - assert(ranges::equal(expected, span{input}.first<4>())); - assert(comparisonCounter == ranges::size(input) - 1); + assert(equal(expected, span{input}.first<4>())); + assert(comparisonCounter == size(input) - 1); } } }; diff --git a/tests/std/tests/P0896R4_ranges_alg_unique_copy/test.cpp b/tests/std/tests/P0896R4_ranges_alg_unique_copy/test.cpp index dd4b68bbe66..870e8a021f3 100644 --- a/tests/std/tests/P0896R4_ranges_alg_unique_copy/test.cpp +++ b/tests/std/tests/P0896R4_ranges_alg_unique_copy/test.cpp @@ -27,7 +27,8 @@ struct instantiator { template static constexpr void call() { - using ranges::unique_copy, ranges::unique_copy_result, ranges::equal, ranges::equal_to, ranges::iterator_t; + using ranges::unique_copy, ranges::unique_copy_result, ranges::equal, ranges::equal_to, ranges::size, + ranges::iterator_t; { // Validate iterator + sentinel overload P output[4] = {{-1, -1}, {-1, -1}, {-1, -1}, {-1, -1}}; @@ -55,17 +56,17 @@ struct instantiator { assert(result.out.peek() == output + 4); assert(equal(expected, output)); if constexpr (input_iterator) { - assert(inputCounter == ranges::size(input) - 1); - assert(outputCounter == ranges::size(input) - 1); + assert(inputCounter == size(input) - 1); + assert(outputCounter == size(input) - 1); assert(storeCounter == 0); } else if constexpr (ranges::forward_range) { - assert(inputCounter == 2 * (ranges::size(input) - 1)); + assert(inputCounter == 2 * (size(input) - 1)); assert(outputCounter == 0); assert(storeCounter == 0); } else { - assert(inputCounter == ranges::size(input) - 1); + assert(inputCounter == size(input) - 1); assert(outputCounter == 0); - assert(storeCounter == ranges::size(input) - 1); + assert(storeCounter == size(input) - 1); } } @@ -94,17 +95,17 @@ struct instantiator { assert(result.out.peek() == output + 4); assert(equal(expected, output)); if constexpr (input_iterator) { - assert(inputCounter == ranges::size(input) - 1); - assert(outputCounter == ranges::size(input) - 1); + assert(inputCounter == size(input) - 1); + assert(outputCounter == size(input) - 1); assert(storeCounter == 0); } else if constexpr (ranges::forward_range) { - assert(inputCounter == 2 * (ranges::size(input) - 1)); + assert(inputCounter == 2 * (size(input) - 1)); assert(outputCounter == 0); assert(storeCounter == 0); } else { - assert(inputCounter == ranges::size(input) - 1); + assert(inputCounter == size(input) - 1); assert(outputCounter == 0); - assert(storeCounter == ranges::size(input) - 1); + assert(storeCounter == size(input) - 1); } } } From 966c6d5075a7ef900b2248e15cd2e7834ae54054 Mon Sep 17 00:00:00 2001 From: Michael Schellenberger Costa Date: Tue, 14 Jul 2020 17:22:37 +0200 Subject: [PATCH 12/20] Fix out of heap error --- .../P0896R4_ranges_alg_unique_copy/test.cpp | 141 +++++++++--------- 1 file changed, 73 insertions(+), 68 deletions(-) diff --git a/tests/std/tests/P0896R4_ranges_alg_unique_copy/test.cpp b/tests/std/tests/P0896R4_ranges_alg_unique_copy/test.cpp index 870e8a021f3..5b6f2e8385f 100644 --- a/tests/std/tests/P0896R4_ranges_alg_unique_copy/test.cpp +++ b/tests/std/tests/P0896R4_ranges_alg_unique_copy/test.cpp @@ -29,89 +29,94 @@ struct instantiator { static constexpr void call() { using ranges::unique_copy, ranges::unique_copy_result, ranges::equal, ranges::equal_to, ranges::size, ranges::iterator_t; +#if !defined(__clang__) && !defined(__EDG__) // TRANSITION, VSO-938163 + if constexpr (!ranges::contiguous_range) +#endif // TRANSITION, VSO-938163 + { + { // Validate iterator + sentinel overload + P output[4] = {{-1, -1}, {-1, -1}, {-1, -1}, {-1, -1}}; + Read wrapped_input{input}; - { // Validate iterator + sentinel overload - P output[4] = {{-1, -1}, {-1, -1}, {-1, -1}, {-1, -1}}; - Read wrapped_input{input}; + // Tests which implementation strategy was chosen + size_t inputCounter = 0; + size_t outputCounter = 0; + size_t storeCounter = 0; + auto countedProjection = [&](const P& val) { + if (begin(output) <= addressof(val) && addressof(val) < end(output)) { + ++outputCounter; + } else if (begin(input) <= addressof(val) && addressof(val) < end(input)) { + ++inputCounter; + } else { + ++storeCounter; + } + return val.second; + }; - // Tests which implementation strategy was chosen - size_t inputCounter = 0; - size_t outputCounter = 0; - size_t storeCounter = 0; - auto countedProjection = [&](const P& val) { - if (output <= addressof(val) && addressof(val) < end(output)) { - ++outputCounter; - } else if (input <= addressof(val) && addressof(val) < end(input)) { - ++inputCounter; + auto result = unique_copy( + wrapped_input.begin(), wrapped_input.end(), Write{output}, equal_to{}, countedProjection); + STATIC_ASSERT(same_as, Write>>); + assert(result.in == wrapped_input.end()); + assert(result.out.peek() == end(output)); + assert(equal(expected, output)); + if constexpr (input_iterator) { + assert(inputCounter == size(input) - 1); + assert(outputCounter == size(input) - 1); + assert(storeCounter == 0); + } else if constexpr (ranges::forward_range) { + assert(inputCounter == 2 * (size(input) - 1)); + assert(outputCounter == 0); + assert(storeCounter == 0); } else { - ++storeCounter; + assert(inputCounter == size(input) - 1); + assert(outputCounter == 0); + assert(storeCounter == size(input) - 1); } - return val.second; - }; - - auto result = - unique_copy(wrapped_input.begin(), wrapped_input.end(), Write{output}, equal_to{}, countedProjection); - STATIC_ASSERT(same_as, Write>>); - assert(result.in == wrapped_input.end()); - assert(result.out.peek() == output + 4); - assert(equal(expected, output)); - if constexpr (input_iterator) { - assert(inputCounter == size(input) - 1); - assert(outputCounter == size(input) - 1); - assert(storeCounter == 0); - } else if constexpr (ranges::forward_range) { - assert(inputCounter == 2 * (size(input) - 1)); - assert(outputCounter == 0); - assert(storeCounter == 0); - } else { - assert(inputCounter == size(input) - 1); - assert(outputCounter == 0); - assert(storeCounter == size(input) - 1); } - } + { // Validate range overload + P output[4] = {{-1, -1}, {-1, -1}, {-1, -1}, {-1, -1}}; + Read wrapped_input{input}; - { // Validate range overload - P output[4] = {{-1, -1}, {-1, -1}, {-1, -1}, {-1, -1}}; - Read wrapped_input{input}; + // Tests which implementation strategy was chosen + size_t inputCounter = 0; + size_t outputCounter = 0; + size_t storeCounter = 0; + auto countedProjection = [&](const P& val) { + if (begin(output) <= addressof(val) && addressof(val) < end(output)) { + ++outputCounter; + } else if (begin(input) <= addressof(val) && addressof(val) < end(input)) { + ++inputCounter; + } else { + ++storeCounter; + } + return val.second; + }; - // Tests which implementation strategy was chosen - size_t inputCounter = 0; - size_t outputCounter = 0; - size_t storeCounter = 0; - auto countedProjection = [&](const P& val) { - if (output <= addressof(val) && addressof(val) < end(output)) { - ++outputCounter; - } else if (input <= addressof(val) && addressof(val) < end(input)) { - ++inputCounter; + auto result = unique_copy(wrapped_input, Write{output}, equal_to{}, countedProjection); + STATIC_ASSERT(same_as, Write>>); + assert(result.in == wrapped_input.end()); + assert(result.out.peek() == end(output)); + assert(equal(expected, output)); + if constexpr (input_iterator) { + assert(inputCounter == size(input) - 1); + assert(outputCounter == size(input) - 1); + assert(storeCounter == 0); + } else if constexpr (ranges::forward_range) { + assert(inputCounter == 2 * (size(input) - 1)); + assert(outputCounter == 0); + assert(storeCounter == 0); } else { - ++storeCounter; + assert(inputCounter == size(input) - 1); + assert(outputCounter == 0); + assert(storeCounter == size(input) - 1); } - return val.second; - }; - - auto result = unique_copy(wrapped_input, Write{output}, equal_to{}, countedProjection); - STATIC_ASSERT(same_as, Write>>); - assert(result.in == wrapped_input.end()); - assert(result.out.peek() == output + 4); - assert(equal(expected, output)); - if constexpr (input_iterator) { - assert(inputCounter == size(input) - 1); - assert(outputCounter == size(input) - 1); - assert(storeCounter == 0); - } else if constexpr (ranges::forward_range) { - assert(inputCounter == 2 * (size(input) - 1)); - assert(outputCounter == 0); - assert(storeCounter == 0); - } else { - assert(inputCounter == size(input) - 1); - assert(outputCounter == 0); - assert(storeCounter == size(input) - 1); } } } }; int main() { +#ifndef _PREFAST_ // TRANSITION, GH-1030 STATIC_ASSERT((test_in_write(), true)); +#endif // TRANSITION, GH-1030 test_in_write(); } From 3e50a240afad30cb3939d67d1d6cdd5ee849365f Mon Sep 17 00:00:00 2001 From: Michael Schellenberger Costa Date: Tue, 14 Jul 2020 18:36:39 +0200 Subject: [PATCH 13/20] Not my day --- tests/std/tests/P0896R4_ranges_alg_unique_copy/test.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/tests/std/tests/P0896R4_ranges_alg_unique_copy/test.cpp b/tests/std/tests/P0896R4_ranges_alg_unique_copy/test.cpp index 5b6f2e8385f..c254bacbfb1 100644 --- a/tests/std/tests/P0896R4_ranges_alg_unique_copy/test.cpp +++ b/tests/std/tests/P0896R4_ranges_alg_unique_copy/test.cpp @@ -30,7 +30,7 @@ struct instantiator { using ranges::unique_copy, ranges::unique_copy_result, ranges::equal, ranges::equal_to, ranges::size, ranges::iterator_t; #if !defined(__clang__) && !defined(__EDG__) // TRANSITION, VSO-938163 - if constexpr (!ranges::contiguous_range) + if constexpr (!ranges::contiguous_range) #endif // TRANSITION, VSO-938163 { { // Validate iterator + sentinel overload From 35b8bcf3464a2373701582240345cd46e734d2c0 Mon Sep 17 00:00:00 2001 From: Michael Schellenberger Costa Date: Tue, 14 Jul 2020 21:43:24 +0200 Subject: [PATCH 14/20] Use intrumented type to check for the used strategy --- .../P0896R4_ranges_alg_unique_copy/test.cpp | 157 +++++++++++------- 1 file changed, 100 insertions(+), 57 deletions(-) diff --git a/tests/std/tests/P0896R4_ranges_alg_unique_copy/test.cpp b/tests/std/tests/P0896R4_ranges_alg_unique_copy/test.cpp index c254bacbfb1..57fc7591ca2 100644 --- a/tests/std/tests/P0896R4_ranges_alg_unique_copy/test.cpp +++ b/tests/std/tests/P0896R4_ranges_alg_unique_copy/test.cpp @@ -10,7 +10,36 @@ #include using namespace std; -using P = pair; + +struct instrumentedPair { + pair _val = {0, 0}; + mutable int _numProjections = 0; + + constexpr instrumentedPair() = default; + constexpr instrumentedPair(const instrumentedPair& other) : _val{other._val} {} + constexpr instrumentedPair(instrumentedPair&& other) : _val{exchange(other._val, {-1, -1})} {} + constexpr instrumentedPair& operator=(const instrumentedPair& other) { + _val = other._val; + _numProjections = 0; + return *this; + } + constexpr instrumentedPair& operator=(instrumentedPair& other) { + _val = exchange(other._val, {-1, -1}); + _numProjections = 0; + return *this; + } + + constexpr instrumentedPair(const int a, const int b) : _val{a, b} {}; + constexpr instrumentedPair(const int a, const int b, const int c) : _val{a, b}, _numProjections{c} {}; + + constexpr auto operator<=>(const instrumentedPair&) const = default; + constexpr bool operator==(const instrumentedPair&) const = default; + + constexpr friend int countedProjection(const instrumentedPair& value) { + ++value._numProjections; + return value._val.second; + } +}; // Validate that unique_copy_result aliases in_out_result STATIC_ASSERT(same_as, ranges::in_out_result>); @@ -22,8 +51,34 @@ STATIC_ASSERT( same_as{}, nullptr_to)), ranges::unique_copy_result>); struct instantiator { - static constexpr P expected[4] = {{0, 99}, {1, 47}, {3, 99}, {4, 47}}; - static constexpr P input[6] = {{0, 99}, {1, 47}, {2, 47}, {3, 99}, {4, 47}, {5, 47}}; + static constexpr instrumentedPair expectedOutput[4] = { + instrumentedPair{0, 99, 0}, // + instrumentedPair{1, 47, 0}, // + instrumentedPair{3, 99, 0}, // + instrumentedPair{4, 47, 0} // + }; + static constexpr instrumentedPair expectedOutputRead[4] = { + instrumentedPair{0, 99, 1}, // + instrumentedPair{1, 47, 2}, // + instrumentedPair{3, 99, 1}, // + instrumentedPair{4, 47, 1} // + }; + static constexpr instrumentedPair expectedInput[6] = { + instrumentedPair{0, 99, 0}, // + instrumentedPair{1, 47, 1}, // + instrumentedPair{2, 47, 1}, // + instrumentedPair{3, 99, 1}, // + instrumentedPair{4, 47, 1}, // + instrumentedPair{5, 47, 1} // + }; + static constexpr instrumentedPair expectedInputRead[6] = { + instrumentedPair{0, 99, 1}, // + instrumentedPair{1, 47, 3}, // + instrumentedPair{2, 47, 1}, // + instrumentedPair{3, 99, 2}, // + instrumentedPair{4, 47, 2}, // + instrumentedPair{5, 47, 1} // + }; template static constexpr void call() { @@ -34,80 +89,68 @@ struct instantiator { #endif // TRANSITION, VSO-938163 { { // Validate iterator + sentinel overload - P output[4] = {{-1, -1}, {-1, -1}, {-1, -1}, {-1, -1}}; - Read wrapped_input{input}; - - // Tests which implementation strategy was chosen - size_t inputCounter = 0; - size_t outputCounter = 0; - size_t storeCounter = 0; - auto countedProjection = [&](const P& val) { - if (begin(output) <= addressof(val) && addressof(val) < end(output)) { - ++outputCounter; - } else if (begin(input) <= addressof(val) && addressof(val) < end(input)) { - ++inputCounter; - } else { - ++storeCounter; - } - return val.second; + const instrumentedPair input[6] = { + instrumentedPair{0, 99}, // + instrumentedPair{1, 47}, // + instrumentedPair{2, 47}, // + instrumentedPair{3, 99}, // + instrumentedPair{4, 47}, // + instrumentedPair{5, 47} // }; + instrumentedPair output[4] = { + instrumentedPair{-1, -1}, // + instrumentedPair{-1, -1}, // + instrumentedPair{-1, -1}, // + instrumentedPair{-1, -1} // + }; + Read wrapped_input{input}; auto result = unique_copy( wrapped_input.begin(), wrapped_input.end(), Write{output}, equal_to{}, countedProjection); STATIC_ASSERT(same_as, Write>>); assert(result.in == wrapped_input.end()); assert(result.out.peek() == end(output)); - assert(equal(expected, output)); if constexpr (input_iterator) { - assert(inputCounter == size(input) - 1); - assert(outputCounter == size(input) - 1); - assert(storeCounter == 0); + assert(equal(expectedOutputRead, output)); + assert(equal(expectedInput, input)); } else if constexpr (ranges::forward_range) { - assert(inputCounter == 2 * (size(input) - 1)); - assert(outputCounter == 0); - assert(storeCounter == 0); + assert(equal(expectedOutput, output)); + assert(equal(expectedInputRead, input)); } else { - assert(inputCounter == size(input) - 1); - assert(outputCounter == 0); - assert(storeCounter == size(input) - 1); + assert(equal(expectedOutput, output)); + assert(equal(expectedInput, input)); } } { // Validate range overload - P output[4] = {{-1, -1}, {-1, -1}, {-1, -1}, {-1, -1}}; - Read wrapped_input{input}; - - // Tests which implementation strategy was chosen - size_t inputCounter = 0; - size_t outputCounter = 0; - size_t storeCounter = 0; - auto countedProjection = [&](const P& val) { - if (begin(output) <= addressof(val) && addressof(val) < end(output)) { - ++outputCounter; - } else if (begin(input) <= addressof(val) && addressof(val) < end(input)) { - ++inputCounter; - } else { - ++storeCounter; - } - return val.second; + const instrumentedPair input[6] = { + instrumentedPair{0, 99}, // + instrumentedPair{1, 47}, // + instrumentedPair{2, 47}, // + instrumentedPair{3, 99}, // + instrumentedPair{4, 47}, // + instrumentedPair{5, 47} // }; + instrumentedPair output[4] = { + instrumentedPair{-1, -1}, // + instrumentedPair{-1, -1}, // + instrumentedPair{-1, -1}, // + instrumentedPair{-1, -1} // + }; + Read wrapped_input{input}; auto result = unique_copy(wrapped_input, Write{output}, equal_to{}, countedProjection); STATIC_ASSERT(same_as, Write>>); assert(result.in == wrapped_input.end()); assert(result.out.peek() == end(output)); - assert(equal(expected, output)); if constexpr (input_iterator) { - assert(inputCounter == size(input) - 1); - assert(outputCounter == size(input) - 1); - assert(storeCounter == 0); + assert(equal(expectedOutputRead, output)); + assert(equal(expectedInput, input)); } else if constexpr (ranges::forward_range) { - assert(inputCounter == 2 * (size(input) - 1)); - assert(outputCounter == 0); - assert(storeCounter == 0); + assert(equal(expectedOutput, output)); + assert(equal(expectedInputRead, input)); } else { - assert(inputCounter == size(input) - 1); - assert(outputCounter == 0); - assert(storeCounter == size(input) - 1); + assert(equal(expectedOutput, output)); + assert(equal(expectedInput, input)); } } } @@ -116,7 +159,7 @@ struct instantiator { int main() { #ifndef _PREFAST_ // TRANSITION, GH-1030 - STATIC_ASSERT((test_in_write(), true)); + STATIC_ASSERT((test_in_write(), true)); #endif // TRANSITION, GH-1030 - test_in_write(); + test_in_write(); } From cfb57d64e38d0b4e4fa448dd54863741b31632fe Mon Sep 17 00:00:00 2001 From: Michael Schellenberger Costa Date: Tue, 14 Jul 2020 23:20:36 +0200 Subject: [PATCH 15/20] The spaceship is grounded --- .../std/tests/P0896R4_ranges_alg_unique_copy/test.cpp | 11 +++++------ 1 file changed, 5 insertions(+), 6 deletions(-) diff --git a/tests/std/tests/P0896R4_ranges_alg_unique_copy/test.cpp b/tests/std/tests/P0896R4_ranges_alg_unique_copy/test.cpp index 57fc7591ca2..d595107fcc9 100644 --- a/tests/std/tests/P0896R4_ranges_alg_unique_copy/test.cpp +++ b/tests/std/tests/P0896R4_ranges_alg_unique_copy/test.cpp @@ -32,13 +32,12 @@ struct instrumentedPair { constexpr instrumentedPair(const int a, const int b) : _val{a, b} {}; constexpr instrumentedPair(const int a, const int b, const int c) : _val{a, b}, _numProjections{c} {}; - constexpr auto operator<=>(const instrumentedPair&) const = default; - constexpr bool operator==(const instrumentedPair&) const = default; + constexpr bool operator==(const instrumentedPair&) const = default; +}; - constexpr friend int countedProjection(const instrumentedPair& value) { - ++value._numProjections; - return value._val.second; - } +constexpr auto countedProjection = [](const instrumentedPair& value) { + ++value._numProjections; + return value._val.second; }; // Validate that unique_copy_result aliases in_out_result From b4fb1f9758831857b77537d9bacd21fe0f4f0c70 Mon Sep 17 00:00:00 2001 From: Michael Schellenberger Costa Date: Wed, 15 Jul 2020 08:15:51 +0200 Subject: [PATCH 16/20] No mutable for you sir --- .../P0896R4_ranges_alg_unique_copy/test.cpp | 16 ++++++++-------- 1 file changed, 8 insertions(+), 8 deletions(-) diff --git a/tests/std/tests/P0896R4_ranges_alg_unique_copy/test.cpp b/tests/std/tests/P0896R4_ranges_alg_unique_copy/test.cpp index d595107fcc9..bef3bc63cd3 100644 --- a/tests/std/tests/P0896R4_ranges_alg_unique_copy/test.cpp +++ b/tests/std/tests/P0896R4_ranges_alg_unique_copy/test.cpp @@ -12,8 +12,8 @@ using namespace std; struct instrumentedPair { - pair _val = {0, 0}; - mutable int _numProjections = 0; + pair _val = {0, 0}; + int _numProjections = 0; constexpr instrumentedPair() = default; constexpr instrumentedPair(const instrumentedPair& other) : _val{other._val} {} @@ -23,7 +23,7 @@ struct instrumentedPair { _numProjections = 0; return *this; } - constexpr instrumentedPair& operator=(instrumentedPair& other) { + constexpr instrumentedPair& operator=(instrumentedPair&& other) { _val = exchange(other._val, {-1, -1}); _numProjections = 0; return *this; @@ -35,7 +35,7 @@ struct instrumentedPair { constexpr bool operator==(const instrumentedPair&) const = default; }; -constexpr auto countedProjection = [](const instrumentedPair& value) { +constexpr auto countedProjection = [](instrumentedPair& value) { ++value._numProjections; return value._val.second; }; @@ -88,7 +88,7 @@ struct instantiator { #endif // TRANSITION, VSO-938163 { { // Validate iterator + sentinel overload - const instrumentedPair input[6] = { + instrumentedPair input[6] = { instrumentedPair{0, 99}, // instrumentedPair{1, 47}, // instrumentedPair{2, 47}, // @@ -121,7 +121,7 @@ struct instantiator { } } { // Validate range overload - const instrumentedPair input[6] = { + instrumentedPair input[6] = { instrumentedPair{0, 99}, // instrumentedPair{1, 47}, // instrumentedPair{2, 47}, // @@ -158,7 +158,7 @@ struct instantiator { int main() { #ifndef _PREFAST_ // TRANSITION, GH-1030 - STATIC_ASSERT((test_in_write(), true)); + STATIC_ASSERT((test_in_write(), true)); #endif // TRANSITION, GH-1030 - test_in_write(); + test_in_write(); } From 6b54bde1ddbe268fc7793f0f2257e11a62aa812b Mon Sep 17 00:00:00 2001 From: Michael Schellenberger Costa Date: Fri, 17 Jul 2020 13:32:19 +0200 Subject: [PATCH 17/20] Reduce the test cases to the interesting ones --- .../P0896R4_ranges_alg_unique_copy/test.cpp | 34 +++++++++++++++++++ 1 file changed, 34 insertions(+) diff --git a/tests/std/tests/P0896R4_ranges_alg_unique_copy/test.cpp b/tests/std/tests/P0896R4_ranges_alg_unique_copy/test.cpp index bef3bc63cd3..cfd111d4560 100644 --- a/tests/std/tests/P0896R4_ranges_alg_unique_copy/test.cpp +++ b/tests/std/tests/P0896R4_ranges_alg_unique_copy/test.cpp @@ -156,9 +156,43 @@ struct instantiator { } }; +#ifdef TEST_EVERYTHING int main() { #ifndef _PREFAST_ // TRANSITION, GH-1030 STATIC_ASSERT((test_in_write(), true)); #endif // TRANSITION, GH-1030 test_in_write(); } +#else // ^^^ test all range combinations // test only interesting range combos vvv + +// We need to test the three different implementations, so we need input_range/forward_range as input and +// output_iterator/input_iterator as output. +using in_test_range = test::range; + +using fwd_test_range = test::range; + +using out_test_iterator = test::iterator; + +using in_test_iterator = test::iterator; + +constexpr bool run_tests() { + // Reread output implementation + instantiator::call(); + + // Reread input implementation + instantiator::call(); + + // Store implementation + instantiator::call(); + return true; +} + +int main() { + STATIC_ASSERT(run_tests()); + run_tests(); +} +#endif // TEST_EVERYTHING From 2adc3bad6fdafc7c9b00b0d9efc516454724b8c9 Mon Sep 17 00:00:00 2001 From: Casey Carter Date: Tue, 21 Jul 2020 08:38:15 -0700 Subject: [PATCH 18/20] Simplify test code and workaround tasty compiler bugs --- stl/inc/algorithm | 2 +- .../tests/P0896R4_ranges_alg_unique/test.cpp | 61 +++-- .../P0896R4_ranges_alg_unique_copy/test.cpp | 223 ++++++------------ 3 files changed, 110 insertions(+), 176 deletions(-) diff --git a/stl/inc/algorithm b/stl/inc/algorithm index be18157dbde..eaa9d34fe1a 100644 --- a/stl/inc/algorithm +++ b/stl/inc/algorithm @@ -4626,9 +4626,9 @@ namespace ranges { _STL_INTERNAL_STATIC_ASSERT(input_iterator<_It>); _STL_INTERNAL_STATIC_ASSERT(sentinel_for<_Se, _It>); _STL_INTERNAL_STATIC_ASSERT(weakly_incrementable<_Out>); + _STL_INTERNAL_STATIC_ASSERT(indirect_equivalence_relation<_Pr, projected<_It, _Pj>>); _STL_INTERNAL_STATIC_ASSERT(indirectly_copyable<_It, _Out>); _STL_INTERNAL_STATIC_ASSERT(_Can_reread_or_store<_It, _Out>); - _STL_INTERNAL_STATIC_ASSERT(indirect_equivalence_relation<_Pr, projected<_It, _Pj>>); if (_First == _Last) { return {_STD move(_First), _STD move(_Result)}; diff --git a/tests/std/tests/P0896R4_ranges_alg_unique/test.cpp b/tests/std/tests/P0896R4_ranges_alg_unique/test.cpp index 63cb6bff22c..77762601949 100644 --- a/tests/std/tests/P0896R4_ranges_alg_unique/test.cpp +++ b/tests/std/tests/P0896R4_ranges_alg_unique/test.cpp @@ -19,40 +19,49 @@ STATIC_ASSERT(same_as{})), ranges::subran struct instantiator { static constexpr P expected[4] = {{0, 99}, {1, 47}, {3, 99}, {4, 47}}; + static constexpr auto make_counter(size_t& count) { + return [&count](const int a, const int b) { + ++count; + return a == b; + }; + } + template static constexpr void call() { - using ranges::unique, ranges::subrange, ranges::equal, ranges::size, ranges::iterator_t; +#if !defined(__clang__) && !defined(__EDG__) // TRANSITION, VSO-938163 + if constexpr (!ranges::contiguous_range) +#endif // TRANSITION, VSO-938163 + { + using ranges::unique, ranges::subrange, ranges::equal, ranges::size, ranges::iterator_t; - size_t comparisonCounter = 0; - auto countedEq = [&comparisonCounter](const int a, const int b) { - ++comparisonCounter; - return a == b; - }; + size_t comparisonCounter = 0; + const auto countedEq = make_counter(comparisonCounter); - { // Validate iterator + sentinel overload - P input[6] = {{0, 99}, {1, 47}, {2, 47}, {3, 99}, {4, 47}, {5, 47}}; - ReadWrite wrapped_input{input}; + { // Validate iterator + sentinel overload + P input[6] = {{0, 99}, {1, 47}, {2, 47}, {3, 99}, {4, 47}, {5, 47}}; + ReadWrite wrapped_input{input}; - auto result = unique(wrapped_input.begin(), wrapped_input.end(), countedEq, get_second); - STATIC_ASSERT(same_as>>); - assert(result.begin() == next(wrapped_input.begin(), 4)); - assert(result.end() == wrapped_input.end()); - assert(equal(expected, span{input}.first<4>())); - assert(comparisonCounter == size(input) - 1); - } + auto result = unique(wrapped_input.begin(), wrapped_input.end(), countedEq, get_second); + STATIC_ASSERT(same_as>>); + assert(result.begin() == next(wrapped_input.begin(), 4)); + assert(result.end() == wrapped_input.end()); + assert(equal(expected, span{input}.first<4>())); + assert(comparisonCounter == size(input) - 1); + } - comparisonCounter = 0; + comparisonCounter = 0; - { // Validate range overload - P input[6] = {{0, 99}, {1, 47}, {2, 47}, {3, 99}, {4, 47}, {5, 47}}; - ReadWrite wrapped_input{input}; + { // Validate range overload + P input[6] = {{0, 99}, {1, 47}, {2, 47}, {3, 99}, {4, 47}, {5, 47}}; + ReadWrite wrapped_input{input}; - auto result = unique(wrapped_input, countedEq, get_second); - STATIC_ASSERT(same_as>>); - assert(result.begin() == next(wrapped_input.begin(), 4)); - assert(result.end() == wrapped_input.end()); - assert(equal(expected, span{input}.first<4>())); - assert(comparisonCounter == size(input) - 1); + auto result = unique(wrapped_input, countedEq, get_second); + STATIC_ASSERT(same_as>>); + assert(result.begin() == next(wrapped_input.begin(), 4)); + assert(result.end() == wrapped_input.end()); + assert(equal(expected, span{input}.first<4>())); + assert(comparisonCounter == size(input) - 1); + } } } }; diff --git a/tests/std/tests/P0896R4_ranges_alg_unique_copy/test.cpp b/tests/std/tests/P0896R4_ranges_alg_unique_copy/test.cpp index cfd111d4560..265a0daa0b5 100644 --- a/tests/std/tests/P0896R4_ranges_alg_unique_copy/test.cpp +++ b/tests/std/tests/P0896R4_ranges_alg_unique_copy/test.cpp @@ -5,40 +5,11 @@ #include #include #include -#include #include #include using namespace std; - -struct instrumentedPair { - pair _val = {0, 0}; - int _numProjections = 0; - - constexpr instrumentedPair() = default; - constexpr instrumentedPair(const instrumentedPair& other) : _val{other._val} {} - constexpr instrumentedPair(instrumentedPair&& other) : _val{exchange(other._val, {-1, -1})} {} - constexpr instrumentedPair& operator=(const instrumentedPair& other) { - _val = other._val; - _numProjections = 0; - return *this; - } - constexpr instrumentedPair& operator=(instrumentedPair&& other) { - _val = exchange(other._val, {-1, -1}); - _numProjections = 0; - return *this; - } - - constexpr instrumentedPair(const int a, const int b) : _val{a, b} {}; - constexpr instrumentedPair(const int a, const int b, const int c) : _val{a, b}, _numProjections{c} {}; - - constexpr bool operator==(const instrumentedPair&) const = default; -}; - -constexpr auto countedProjection = [](instrumentedPair& value) { - ++value._numProjections; - return value._val.second; -}; +using P = std::pair; // Validate that unique_copy_result aliases in_out_result STATIC_ASSERT(same_as, ranges::in_out_result>); @@ -49,109 +20,59 @@ STATIC_ASSERT(same_as{}, nullptr_to STATIC_ASSERT( same_as{}, nullptr_to)), ranges::unique_copy_result>); -struct instantiator { - static constexpr instrumentedPair expectedOutput[4] = { - instrumentedPair{0, 99, 0}, // - instrumentedPair{1, 47, 0}, // - instrumentedPair{3, 99, 0}, // - instrumentedPair{4, 47, 0} // - }; - static constexpr instrumentedPair expectedOutputRead[4] = { - instrumentedPair{0, 99, 1}, // - instrumentedPair{1, 47, 2}, // - instrumentedPair{3, 99, 1}, // - instrumentedPair{4, 47, 1} // - }; - static constexpr instrumentedPair expectedInput[6] = { - instrumentedPair{0, 99, 0}, // - instrumentedPair{1, 47, 1}, // - instrumentedPair{2, 47, 1}, // - instrumentedPair{3, 99, 1}, // - instrumentedPair{4, 47, 1}, // - instrumentedPair{5, 47, 1} // - }; - static constexpr instrumentedPair expectedInputRead[6] = { - instrumentedPair{0, 99, 1}, // - instrumentedPair{1, 47, 3}, // - instrumentedPair{2, 47, 1}, // - instrumentedPair{3, 99, 2}, // - instrumentedPair{4, 47, 2}, // - instrumentedPair{5, 47, 1} // - }; +constexpr P expectedInputRead[6] = {{0, 99}, {1, 47}, {1, 47}, {1, 99}, {1, 47}, {1, 47}}; +constexpr P expectedOutputRead[4] = {{1, 99}, {3, 47}, {2, 99}, {2, 47}}; +constexpr P expectedInput[6] = {{1, 99}, {3, 47}, {1, 47}, {2, 99}, {2, 47}, {1, 47}}; +constexpr P expectedOutput[4] = {{0, 99}, {1, 47}, {1, 99}, {1, 47}}; +constexpr auto countedProjection = [](P& value) { + ++value.first; + return value.second; +}; + +struct test_iterator_overload { + template + static constexpr void call() { + using ranges::unique_copy, ranges::unique_copy_result, ranges::equal, ranges::equal_to, ranges::iterator_t, + ranges::size; + // Validate iterator + sentinel overload + P input[6] = {{0, 99}, {0, 47}, {0, 47}, {0, 99}, {0, 47}, {0, 47}}; + P output[4] = {{-1, -1}, {-1, -1}, {-1, -1}, {-1, -1}}; + Read wrapped_input{input}; + + const same_as, Write>> auto result = + unique_copy(wrapped_input.begin(), wrapped_input.end(), Write{output}, equal_to{}, countedProjection); + assert(result.in == wrapped_input.end()); + assert(result.out.peek() == end(output)); + if constexpr (input_iterator || !ranges::forward_range) { + assert(equal(output, expectedOutputRead)); + assert(equal(input, expectedInputRead)); + } else { + assert(equal(output, expectedOutput)); + assert(equal(input, expectedInput)); + } + } +}; +struct test_range_overload { template static constexpr void call() { - using ranges::unique_copy, ranges::unique_copy_result, ranges::equal, ranges::equal_to, ranges::size, - ranges::iterator_t; -#if !defined(__clang__) && !defined(__EDG__) // TRANSITION, VSO-938163 - if constexpr (!ranges::contiguous_range) -#endif // TRANSITION, VSO-938163 - { - { // Validate iterator + sentinel overload - instrumentedPair input[6] = { - instrumentedPair{0, 99}, // - instrumentedPair{1, 47}, // - instrumentedPair{2, 47}, // - instrumentedPair{3, 99}, // - instrumentedPair{4, 47}, // - instrumentedPair{5, 47} // - }; - instrumentedPair output[4] = { - instrumentedPair{-1, -1}, // - instrumentedPair{-1, -1}, // - instrumentedPair{-1, -1}, // - instrumentedPair{-1, -1} // - }; - Read wrapped_input{input}; - - auto result = unique_copy( - wrapped_input.begin(), wrapped_input.end(), Write{output}, equal_to{}, countedProjection); - STATIC_ASSERT(same_as, Write>>); - assert(result.in == wrapped_input.end()); - assert(result.out.peek() == end(output)); - if constexpr (input_iterator) { - assert(equal(expectedOutputRead, output)); - assert(equal(expectedInput, input)); - } else if constexpr (ranges::forward_range) { - assert(equal(expectedOutput, output)); - assert(equal(expectedInputRead, input)); - } else { - assert(equal(expectedOutput, output)); - assert(equal(expectedInput, input)); - } - } - { // Validate range overload - instrumentedPair input[6] = { - instrumentedPair{0, 99}, // - instrumentedPair{1, 47}, // - instrumentedPair{2, 47}, // - instrumentedPair{3, 99}, // - instrumentedPair{4, 47}, // - instrumentedPair{5, 47} // - }; - instrumentedPair output[4] = { - instrumentedPair{-1, -1}, // - instrumentedPair{-1, -1}, // - instrumentedPair{-1, -1}, // - instrumentedPair{-1, -1} // - }; - Read wrapped_input{input}; - - auto result = unique_copy(wrapped_input, Write{output}, equal_to{}, countedProjection); - STATIC_ASSERT(same_as, Write>>); - assert(result.in == wrapped_input.end()); - assert(result.out.peek() == end(output)); - if constexpr (input_iterator) { - assert(equal(expectedOutputRead, output)); - assert(equal(expectedInput, input)); - } else if constexpr (ranges::forward_range) { - assert(equal(expectedOutput, output)); - assert(equal(expectedInputRead, input)); - } else { - assert(equal(expectedOutput, output)); - assert(equal(expectedInput, input)); - } - } + using ranges::unique_copy, ranges::unique_copy_result, ranges::equal, ranges::equal_to, ranges::iterator_t, + ranges::size; + // Validate range overload + P input[6] = {{0, 99}, {0, 47}, {0, 47}, {0, 99}, {0, 47}, {0, 47}}; + P output[4] = {{-1, -1}, {-1, -1}, {-1, -1}, {-1, -1}}; + Read wrapped_input{input}; + + const same_as, Write>> auto result = + unique_copy(wrapped_input, Write{output}, equal_to{}, countedProjection); + assert(result.in == wrapped_input.end()); + assert(result.out.peek() == end(output)); + if constexpr (input_iterator || !ranges::forward_range) { + assert(equal(output, expectedOutputRead)); + assert(equal(input, expectedInputRead)); + } else { + assert(equal(output, expectedOutput)); + assert(equal(input, expectedInput)); } } }; @@ -159,35 +80,39 @@ struct instantiator { #ifdef TEST_EVERYTHING int main() { #ifndef _PREFAST_ // TRANSITION, GH-1030 - STATIC_ASSERT((test_in_write(), true)); + STATIC_ASSERT((test_in_write(), true)); + STATIC_ASSERT((test_in_write(), true)); #endif // TRANSITION, GH-1030 - test_in_write(); + test_in_write(); + test_in_write(); } -#else // ^^^ test all range combinations // test only interesting range combos vvv - -// We need to test the three different implementations, so we need input_range/forward_range as input and -// output_iterator/input_iterator as output. -using in_test_range = test::range; - -using fwd_test_range = test::range; +#else // ^^^ test all range combinations / test only interesting range combos vvv +constexpr bool run_tests() { + // We need to test the three different implementations, so we need input_range/forward_range as input and + // output_iterator/input_iterator as output. + using namespace test; + using test::iterator, test::range; -using out_test_iterator = test::iterator; + using in_test_range = + range; + using fwd_test_range = + range; -using in_test_iterator = test::iterator; + using out_test_iterator = iterator; + using in_test_iterator = iterator; -constexpr bool run_tests() { // Reread output implementation - instantiator::call(); + test_iterator_overload::call(); + test_range_overload::call(); // Reread input implementation - instantiator::call(); + test_iterator_overload::call(); + test_range_overload::call(); // Store implementation - instantiator::call(); + test_iterator_overload::call(); + test_range_overload::call(); + return true; } From b85e0f4fc5da245439700a72175a33052ed10fca Mon Sep 17 00:00:00 2001 From: Casey Carter Date: Tue, 21 Jul 2020 08:51:01 -0700 Subject: [PATCH 19/20] Revert inadvertent vcpkg submodule reference change --- vcpkg | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/vcpkg b/vcpkg index 53f1ae86d03..6185aa76504 160000 --- a/vcpkg +++ b/vcpkg @@ -1 +1 @@ -Subproject commit 53f1ae86d038c0ccf7edbbcfda1eaf6c6d271e9e +Subproject commit 6185aa76504a5025f36754324abf307cc776f3da From 6f6e8746efeb3322130adbfd5bad2e7a13accfea Mon Sep 17 00:00:00 2001 From: Casey Carter Date: Tue, 21 Jul 2020 21:56:06 -0700 Subject: [PATCH 20/20] Stephan's code review comments Co-authored-by: Stephan T. Lavavej --- tests/std/tests/P0896R4_ranges_alg_unique/test.cpp | 6 +++--- tests/std/tests/P0896R4_ranges_alg_unique_copy/test.cpp | 2 +- 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/tests/std/tests/P0896R4_ranges_alg_unique/test.cpp b/tests/std/tests/P0896R4_ranges_alg_unique/test.cpp index 77762601949..3da1a4923e9 100644 --- a/tests/std/tests/P0896R4_ranges_alg_unique/test.cpp +++ b/tests/std/tests/P0896R4_ranges_alg_unique/test.cpp @@ -19,9 +19,9 @@ STATIC_ASSERT(same_as{})), ranges::subran struct instantiator { static constexpr P expected[4] = {{0, 99}, {1, 47}, {3, 99}, {4, 47}}; - static constexpr auto make_counter(size_t& count) { - return [&count](const int a, const int b) { - ++count; + static constexpr auto make_counter(size_t& counter) { + return [&counter](const int a, const int b) { + ++counter; return a == b; }; } diff --git a/tests/std/tests/P0896R4_ranges_alg_unique_copy/test.cpp b/tests/std/tests/P0896R4_ranges_alg_unique_copy/test.cpp index 265a0daa0b5..71e6a268aee 100644 --- a/tests/std/tests/P0896R4_ranges_alg_unique_copy/test.cpp +++ b/tests/std/tests/P0896R4_ranges_alg_unique_copy/test.cpp @@ -9,7 +9,7 @@ #include using namespace std; -using P = std::pair; +using P = pair; // Validate that unique_copy_result aliases in_out_result STATIC_ASSERT(same_as, ranges::in_out_result>);