Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Modernize ranges::swap_ranges and ranges::distance #1062

Merged
merged 9 commits into from
Aug 1, 2020
41 changes: 31 additions & 10 deletions stl/inc/algorithm
Original file line number Diff line number Diff line change
Expand Up @@ -3402,26 +3402,47 @@ namespace ranges {
_It1 _First1, _Se1 _Last1, _It2 _First2, _Se2 _Last2) const {
_Adl_verify_range(_First1, _Last1);
_Adl_verify_range(_First2, _Last2);
auto _UFirst1 = _Get_unwrapped(_STD move(_First1));
auto _ULast1 = _Get_unwrapped(_STD move(_Last1));
auto _UFirst2 = _Get_unwrapped(_STD move(_First2));
auto _ULast2 = _Get_unwrapped(_STD move(_Last2));
for (; _UFirst1 != _ULast1 && _UFirst2 != _ULast2; ++_UFirst1, (void) ++_UFirst2) {
_RANGES iter_swap(_UFirst1, _UFirst2);
}

_Seek_wrapped(_First1, _STD move(_UFirst1));
_Seek_wrapped(_First2, _STD move(_UFirst2));
auto _UResult =
_Swap_ranges_unchecked(_Get_unwrapped(_STD move(_First1)), _Get_unwrapped(_STD move(_Last1)),
_Get_unwrapped(_STD move(_First2)), _Get_unwrapped(_STD move(_Last2)));

_Seek_wrapped(_First1, _STD move(_UResult.in1));
_Seek_wrapped(_First2, _STD move(_UResult.in2));
return {_STD move(_First1), _STD move(_First2)};
}

template <input_range _Rng1, input_range _Rng2>
requires indirectly_swappable<iterator_t<_Rng1>, iterator_t<_Rng2>>
constexpr swap_ranges_result<borrowed_iterator_t<_Rng1>, borrowed_iterator_t<_Rng2>> operator()(
_Rng1&& _Range1, _Rng2&& _Range2) const {
return (*this)(_RANGES begin(_Range1), _RANGES end(_Range1), _RANGES begin(_Range2), _RANGES end(_Range2));
auto _First1 = _RANGES begin(_Range1);
auto _First2 = _RANGES begin(_Range2);

auto _UResult = _Swap_ranges_unchecked(
_Get_unwrapped(_STD move(_First1)), _Uend(_Range1), _Get_unwrapped(_STD move(_First2)), _Uend(_Range2));

_Seek_wrapped(_First1, _STD move(_UResult.in1));
_Seek_wrapped(_First2, _STD move(_UResult.in2));
return {_STD move(_First1), _STD move(_First2)};
}
// clang-format on
private:
template <class _It1, class _Se1, class _It2, class _Se2>
_NODISCARD static constexpr swap_ranges_result<_It1, _It2> _Swap_ranges_unchecked(
_It1 _First1, const _Se1 _Last1, _It2 _First2, const _Se2 _Last2) {
_STL_INTERNAL_STATIC_ASSERT(input_iterator<_It1>);
_STL_INTERNAL_STATIC_ASSERT(sentinel_for<_Se1, _It1>);
_STL_INTERNAL_STATIC_ASSERT(input_iterator<_It2>);
_STL_INTERNAL_STATIC_ASSERT(sentinel_for<_Se2, _It2>);
_STL_INTERNAL_STATIC_ASSERT(indirectly_swappable<_It1, _It2>);

for (; _First1 != _Last1 && _First2 != _Last2; ++_First1, (void) ++_First2) {
_RANGES iter_swap(_First1, _First2);
}

return {_STD move(_First1), _STD move(_First2)};
}
};

inline constexpr _Swap_ranges_fn swap_ranges{_Not_quite_object::_Construct_tag{}};
Expand Down
29 changes: 18 additions & 11 deletions stl/inc/xutility
Original file line number Diff line number Diff line change
Expand Up @@ -3243,25 +3243,32 @@ namespace ranges {
return _Last - _First;
} else {
_Adl_verify_range(_First, _Last);
auto _UFirst = _Get_unwrapped(static_cast<_It&&>(_First));
const auto _ULast = _Get_unwrapped(static_cast<_Se&&>(_Last));
iter_difference_t<_It> _Count = 0;
for (; _UFirst != _ULast; ++_UFirst) {
++_Count;
}

return _Count;
return _Distance_unchecked(_Get_unwrapped(_STD move(_First)), _Get_unwrapped(_STD move(_Last)));
}
}

template <range _Rng>
_NODISCARD constexpr range_difference_t<_Rng> operator()(_Rng&& _Val) const {
_NODISCARD constexpr range_difference_t<_Rng> operator()(_Rng&& _Range) const {
if constexpr (sized_range<_Rng>) {
return static_cast<range_difference_t<_Rng>>(_RANGES size(_Val));
return static_cast<range_difference_t<_Rng>>(_RANGES size(_Range));
} else {
return (*this)(_RANGES begin(_Val), _RANGES end(_Val));
return _Distance_unchecked(_Ubegin(_Range), _Uend(_Range));
}
}

private:
template <class _It, class _Se>
_NODISCARD static constexpr iter_difference_t<_It> _Distance_unchecked(_It _First, const _Se _Last) {
_STL_INTERNAL_STATIC_ASSERT(input_or_output_iterator<_It>);
_STL_INTERNAL_STATIC_ASSERT(sentinel_for<_Se, _It>);

iter_difference_t<_It> _Count = 0;
for (; _First != _Last; ++_First) {
++_Count;
}

return _Count;
}
};

// VARIABLE ranges::distance
Expand Down
2 changes: 1 addition & 1 deletion tests/std/tests/P0896R4_ranges_alg_includes/test.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -68,7 +68,7 @@ int main() {
#else // ^^^ test all permutations of range properties / test only interesting permutations vvv
template <class Category, class Element, test::ProxyRef IsProxyRef>
using test_range = test::range<Category, Element, test::Sized::no, test::CanDifference::no, test::Common::no,
test::CanCompare{derived_from<Category, std::forward_iterator_tag>}, IsProxyRef>;
test::CanCompare{derived_from<Category, forward_iterator_tag>}, IsProxyRef>;

constexpr void run_tests() {
using namespace test;
Expand Down
132 changes: 86 additions & 46 deletions tests/std/tests/P0896R4_ranges_alg_swap_ranges/test.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -2,65 +2,105 @@
// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception

#include <algorithm>
#include <array>
#include <cassert>
#include <concepts>
#include <ranges>
#include <utility>

#include <range_algorithm_support.hpp>
using namespace std;

constexpr void smoke_test() {
using ranges::swap_ranges, ranges::swap_ranges_result, ranges::iterator_t;
using std::same_as;
using I1 = iterator_t<basic_borrowed_range<int>>;
// Validate that swap_ranges_result aliases in_in_result
STATIC_ASSERT(same_as<ranges::swap_ranges_result<int, double>, ranges::in_in_result<int, double>>);

// Validate that swap_ranges_result aliases in_in_result
STATIC_ASSERT(same_as<swap_ranges_result<int, double>, ranges::in_in_result<int, double>>);
// Validate dangling story
STATIC_ASSERT(same_as<decltype(ranges::swap_ranges(borrowed<false>{}, borrowed<false>{})),
ranges::swap_ranges_result<ranges::dangling, ranges::dangling>>);
STATIC_ASSERT(same_as<decltype(ranges::swap_ranges(borrowed<true>{}, borrowed<false>{})),
ranges::swap_ranges_result<int*, ranges::dangling>>);
STATIC_ASSERT(same_as<decltype(ranges::swap_ranges(borrowed<false>{}, borrowed<true>{})),
ranges::swap_ranges_result<ranges::dangling, int*>>);
STATIC_ASSERT(
same_as<decltype(ranges::swap_ranges(borrowed<true>{}, borrowed<true>{})), ranges::swap_ranges_result<int*, int*>>);

{ // Validate ranges overload
using R = std::array<int, 3>;
R range1 = {13, 53, 1876};
R range2 = {34, 243, 9366};
int const expected_output1[] = {34, 243, 9366};
int const expected_output2[] = {13, 53, 1876};
auto result = swap_ranges(basic_borrowed_range{range1}, basic_borrowed_range{range2});
STATIC_ASSERT(same_as<decltype(result), swap_ranges_result<I1, I1>>);
assert(result.in1 == basic_borrowed_range{range1}.end());
assert(result.in2 == basic_borrowed_range{range2}.end());
assert(ranges::equal(range1, expected_output1));
assert(ranges::equal(range2, expected_output2));
}
{ // Validate iterator + sentinel overload
int range1[] = {13, 53, 1876};
int range2[] = {34, 243, 9366};
basic_borrowed_range wrapped_range1{range1};
basic_borrowed_range wrapped_range2{range2};
int const expected_output1[] = {34, 243, 9366};
int const expected_output2[] = {13, 53, 1876};
auto result =
swap_ranges(wrapped_range1.begin(), wrapped_range1.end(), wrapped_range2.begin(), wrapped_range2.end());
STATIC_ASSERT(same_as<decltype(result), swap_ranges_result<I1, I1>>);
assert(result.in1 == wrapped_range1.end());
assert(result.in2 == wrapped_range2.end());
assert(ranges::equal(range1, expected_output1));
assert(ranges::equal(range2, expected_output2));
struct instantiator {
static constexpr int expected_output1[3] = {34, 243, 9366};
static constexpr int expected_output2[3] = {13, 53, 1876};

template <ranges::input_range ReadWrite1, ranges::input_range ReadWrite2>
static constexpr void call() {
using ranges::swap_ranges, ranges::swap_ranges_result, ranges::iterator_t, ranges::equal;
{ // Validate iterator + sentinel overload
int input1[3] = {13, 53, 1876};
int input2[3] = {34, 243, 9366};
ReadWrite1 wrapped_input1{input1};
ReadWrite2 wrapped_input2{input2};

auto result =
swap_ranges(wrapped_input1.begin(), wrapped_input1.end(), wrapped_input2.begin(), wrapped_input2.end());
STATIC_ASSERT(
same_as<decltype(result), swap_ranges_result<iterator_t<ReadWrite1>, iterator_t<ReadWrite2>>>);
assert(result.in1 == wrapped_input1.end());
assert(result.in2 == wrapped_input2.end());
assert(equal(input1, expected_output1));
assert(equal(input2, expected_output2));
}
{ // Validate range overload
int input1[3] = {13, 53, 1876};
int input2[3] = {34, 243, 9366};
ReadWrite1 wrapped_input1{input1};
ReadWrite2 wrapped_input2{input2};

auto result = swap_ranges(wrapped_input1, wrapped_input2);
STATIC_ASSERT(
same_as<decltype(result), swap_ranges_result<iterator_t<ReadWrite1>, iterator_t<ReadWrite2>>>);
assert(result.in1 == wrapped_input1.end());
assert(result.in2 == wrapped_input2.end());
assert(equal(input1, expected_output1));
assert(equal(input2, expected_output2));
}
}
};

#ifdef TEST_EVERYTHING
int main() {
// no constexpr tests here; the below exceeds constexpr step limits
test_in_in<instantiator, int, int>();
}
#else // ^^^ test all permutations of range properties / test only interesting permutations vvv
template <class Category, test::ProxyRef IsProxyRef>
using test_range = test::range<Category, int, test::Sized::no, test::CanDifference::no, test::Common::no,
test::CanCompare{derived_from<Category, forward_iterator_tag>}, IsProxyRef>;

constexpr void run_tests() {
using namespace test;
// The algorithm is completely oblivious to:
// * categories stronger than input
// * whether the end sentinel is an iterator
// * size information
// * iterator and/or sentinel differencing
// so let's vary proxyness for coverage and add a range of each category out of paranoia.

instantiator::call<test_range<input, ProxyRef::yes>, test_range<input, ProxyRef::yes>>();
instantiator::call<test_range<input, ProxyRef::yes>, test_range<input, ProxyRef::no>>();
instantiator::call<test_range<input, ProxyRef::no>, test_range<input, ProxyRef::yes>>();
instantiator::call<test_range<input, ProxyRef::no>, test_range<input, ProxyRef::no>>();

instantiator::call<test_range<input, ProxyRef::yes>, test_range<fwd, ProxyRef::yes>>();
instantiator::call<test_range<input, ProxyRef::yes>, test_range<bidi, ProxyRef::yes>>();
instantiator::call<test_range<input, ProxyRef::yes>, test_range<random, ProxyRef::yes>>();
instantiator::call<test_range<input, ProxyRef::yes>, test_range<contiguous, ProxyRef::no>>();

instantiator::call<test_range<fwd, ProxyRef::yes>, test_range<input, ProxyRef::yes>>();
instantiator::call<test_range<bidi, ProxyRef::yes>, test_range<input, ProxyRef::yes>>();
instantiator::call<test_range<random, ProxyRef::yes>, test_range<input, ProxyRef::yes>>();
instantiator::call<test_range<contiguous, ProxyRef::no>, test_range<input, ProxyRef::yes>>();
}

int main() {
#if defined(__clang__) || defined(__EDG__) // TRANSITION, VSO-938163
STATIC_ASSERT((smoke_test(), true));
STATIC_ASSERT((run_tests(), true));
#endif // TRANSITION, VSO-938163
smoke_test();
run_tests();
}

struct instantiator {
template <class In1, class In2>
static void call(In1 in1 = {}, In2 in2 = {}) {
(void) ranges::swap_ranges(in1, in2);
(void) ranges::swap_ranges(ranges::begin(in1), ranges::end(in1), ranges::begin(in2), ranges::end(in2));
}
};

template void test_in_in<instantiator, int, int>();
#endif // TEST_EVERYTHING