Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
71 changes: 71 additions & 0 deletions stl/inc/algorithm
Original file line number Diff line number Diff line change
Expand Up @@ -3567,6 +3567,77 @@ _FwdIt2 unique_copy(_ExPo&&, _FwdIt1 _First, _FwdIt1 _Last, _FwdIt2 _Dest) noexc
}
#endif // _HAS_CXX17

#ifdef __cpp_lib_concepts
namespace ranges {
// VARIABLE ranges::reverse
// clang-format off
// concept-constrained for strict enforcement as it is used by several algorithms
template <bidirectional_iterator _It>
requires permutable<_It>
constexpr void _Reverse_common(_It _First, _It _Last) {
#if _USE_STD_VECTOR_ALGORITHMS
if constexpr (contiguous_iterator<_It>) {
using _Elem = remove_reference_t<iter_reference_t<_It>>;
constexpr size_t _Nx = sizeof(_Elem);
constexpr bool _Allow_vectorization =
conjunction_v<_Is_trivially_swappable<_Elem>, negation<is_volatile<_Elem>>>;

if constexpr (_Allow_vectorization && _Nx <= 8 && (_Nx & (_Nx - 1)) == 0) {
if (!_STD is_constant_evaluated()) {
_Elem* const _First_addr = _STD to_address(_First);
_Elem* const _Last_addr = _STD to_address(_Last);
if constexpr (_Nx == 1) {
__std_reverse_trivially_swappable_1(_First_addr, _Last_addr);
} else if constexpr (_Nx == 2) {
__std_reverse_trivially_swappable_2(_First_addr, _Last_addr);
} else if constexpr (_Nx == 4) {
__std_reverse_trivially_swappable_4(_First_addr, _Last_addr);
} else {
__std_reverse_trivially_swappable_8(_First_addr, _Last_addr);
}

return;
}
}
}
#endif // _USE_STD_VECTOR_ALGORITHMS

for (; _First != _Last && _First != --_Last; ++_First) {
_RANGES iter_swap(_First, _Last);
}
}
// clang-format on

class _Reverse_fn : private _Not_quite_object {
public:
using _Not_quite_object::_Not_quite_object;

// clang-format off
template <bidirectional_iterator _It, sentinel_for<_It> _Se>
requires permutable<_It>
constexpr _It operator()(_It _First, _Se _Last) const {
_Adl_verify_range(_First, _Last);
auto _UFirst = _Get_unwrapped(_STD move(_First));
auto _ULast = _Get_final_iterator_unwrapped<_It>(_UFirst, _STD move(_Last));
_Seek_wrapped(_First, _ULast);
_RANGES _Reverse_common(_STD move(_UFirst), _STD move(_ULast));
return _First;
}

template <bidirectional_range _Rng>
requires permutable<iterator_t<_Rng>>
constexpr borrowed_iterator_t<_Rng> operator()(_Rng&& _Range) const {
auto _ULast = _Get_final_iterator_unwrapped(_Range);
_RANGES _Reverse_common(_Ubegin(_Range), _ULast);
return _Rewrap_iterator(_Range, _STD move(_ULast));
}
// clang-format on
};

inline constexpr _Reverse_fn reverse{_Not_quite_object::_Construct_tag{}};
} // namespace ranges
#endif // __cpp_lib_concepts

// FUNCTION TEMPLATE reverse_copy
template <class _BidIt, class _OutIt>
_CONSTEXPR20 _OutIt reverse_copy(_BidIt _First, _BidIt _Last, _OutIt _Dest) {
Expand Down
1 change: 1 addition & 0 deletions tests/std/test.lst
Original file line number Diff line number Diff line change
Expand Up @@ -262,6 +262,7 @@ tests\P0896R4_ranges_alg_mismatch
tests\P0896R4_ranges_alg_move
tests\P0896R4_ranges_alg_none_of
tests\P0896R4_ranges_alg_replace_if
tests\P0896R4_ranges_alg_reverse
tests\P0896R4_ranges_alg_search
tests\P0896R4_ranges_alg_search_n
tests\P0896R4_ranges_alg_swap_ranges
Expand Down
4 changes: 4 additions & 0 deletions tests/std/tests/P0896R4_ranges_alg_reverse/env.lst
Original file line number Diff line number Diff line change
@@ -0,0 +1,4 @@
# Copyright (c) Microsoft Corporation.
# SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception

RUNALL_INCLUDE ..\concepts_matrix.lst
183 changes: 183 additions & 0 deletions tests/std/tests/P0896R4_ranges_alg_reverse/test.cpp
Original file line number Diff line number Diff line change
@@ -0,0 +1,183 @@
// Copyright (c) Microsoft Corporation.
// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception

#include <algorithm>
#include <cassert>
#include <compare>
#include <concepts>
#include <numeric>
#include <ranges>
#include <utility>

#include <range_algorithm_support.hpp>

using namespace std;

// Validate dangling story
STATIC_ASSERT(same_as<decltype(ranges::reverse(borrowed<false>{})), ranges::dangling>);
STATIC_ASSERT(same_as<decltype(ranges::reverse(borrowed<true>{})), int*>);

struct nontrivial_int {
int val;

constexpr nontrivial_int(int i) noexcept : val{i} {}
constexpr nontrivial_int(const nontrivial_int& that) noexcept : val{that.val} {}
constexpr nontrivial_int& operator=(const nontrivial_int& that) noexcept {
val = that.val;
return *this;
}

auto operator<=>(const nontrivial_int&) const = default;
};

struct instantiator {
static constexpr nontrivial_int expected_odd[] = {1367, 42, 13};
static constexpr nontrivial_int expected_even[] = {1729, 1367, 42, 13};

template <ranges::bidirectional_range R>
static constexpr void call() {
using ranges::reverse, ranges::equal, ranges::iterator_t;

{ // Validate iterator + sentinel overload, odd length
nontrivial_int input[] = {13, 42, 1367};
R wrapped_input{input};
auto result = reverse(wrapped_input.begin(), wrapped_input.end());
STATIC_ASSERT(same_as<decltype(result), iterator_t<R>>);
assert(result == wrapped_input.end());
assert(equal(input, expected_odd));
}
{ // Validate range overload, odd length
nontrivial_int input[] = {13, 42, 1367};
R wrapped_input{input};
auto result = reverse(wrapped_input);
STATIC_ASSERT(same_as<decltype(result), iterator_t<R>>);
assert(result == wrapped_input.end());
assert(equal(input, expected_odd));
}
{ // Validate iterator + sentinel overload, even length
nontrivial_int input[] = {13, 42, 1367, 1729};
R wrapped_input{input};
auto result = reverse(wrapped_input.begin(), wrapped_input.end());
STATIC_ASSERT(same_as<decltype(result), iterator_t<R>>);
assert(result == wrapped_input.end());
assert(equal(input, expected_even));
}
{ // Validate range overload, even length
nontrivial_int input[] = {13, 42, 1367, 1729};
R wrapped_input{input};
auto result = reverse(wrapped_input);
STATIC_ASSERT(same_as<decltype(result), iterator_t<R>>);
assert(result == wrapped_input.end());
assert(equal(input, expected_even));
}
{ // Validate iterator + sentinel overload, empty range
R wrapped_input{};
auto result = reverse(wrapped_input.begin(), wrapped_input.end());
STATIC_ASSERT(same_as<decltype(result), iterator_t<R>>);
assert(result == wrapped_input.end());
}
{ // Validate range overload, empty range
R wrapped_input{};
auto result = reverse(wrapped_input);
STATIC_ASSERT(same_as<decltype(result), iterator_t<R>>);
assert(result == wrapped_input.end());
}
}
};

template <size_t N>
struct bytes {
unsigned char storage[N];

constexpr bytes(unsigned char base) {
iota(storage, storage + N, base);
}

bool operator==(const bytes&) const = default;
};

struct test_vector {
template <ranges::bidirectional_range R>
static constexpr void call() {
using ranges::reverse, ranges::equal, ranges::iterator_t;

{ // Validate iterator + sentinel overload, vectorizable odd length
ranges::range_value_t<R> input[]{0x10, 0x20, 0x30};
R wrapped_input{input};
auto result = reverse(wrapped_input.begin(), wrapped_input.end());
STATIC_ASSERT(same_as<decltype(result), iterator_t<R>>);
assert(result == wrapped_input.end());
assert(equal(input, initializer_list<ranges::range_value_t<R>>{0x30, 0x20, 0x10}));
}
{ // Validate range overload, vectorizable odd length
ranges::range_value_t<R> input[]{0x10, 0x20, 0x30};
R wrapped_input{input};
auto result = reverse(wrapped_input);
STATIC_ASSERT(same_as<decltype(result), iterator_t<R>>);
assert(result == wrapped_input.end());
assert(equal(input, initializer_list<ranges::range_value_t<R>>{0x30, 0x20, 0x10}));
}

{ // Validate iterator + sentinel overload, vectorizable even length
ranges::range_value_t<R> input[]{0x10, 0x20, 0x30, 0x40};
R wrapped_input{input};
auto result = reverse(wrapped_input.begin(), wrapped_input.end());
STATIC_ASSERT(same_as<decltype(result), iterator_t<R>>);
assert(result == wrapped_input.end());
assert(equal(input, initializer_list<ranges::range_value_t<R>>{0x40, 0x30, 0x20, 0x10}));
}
{ // Validate range overload, vectorizable even length
ranges::range_value_t<R> input[]{0x10, 0x20, 0x30, 0x40};
R wrapped_input{input};
auto result = reverse(wrapped_input);
STATIC_ASSERT(same_as<decltype(result), iterator_t<R>>);
assert(result == wrapped_input.end());
assert(equal(input, initializer_list<ranges::range_value_t<R>>{0x40, 0x30, 0x20, 0x10}));
}

{ // Validate iterator + sentinel overload, vectorizable empty
R wrapped_input{};
auto result = reverse(wrapped_input.begin(), wrapped_input.end());
STATIC_ASSERT(same_as<decltype(result), iterator_t<R>>);
assert(result == wrapped_input.end());
}
{ // Validate range overload, vectorizable empty
R wrapped_input{};
auto result = reverse(wrapped_input);
STATIC_ASSERT(same_as<decltype(result), iterator_t<R>>);
assert(result == wrapped_input.end());
}
}
};

int main() {
#if defined(__clang__) || defined(__EDG__) // TRANSITION, VSO-938163
STATIC_ASSERT((test_bidi<instantiator, nontrivial_int>(), true));
#endif // TRANSITION, VSO-938163
test_bidi<instantiator, nontrivial_int>();

#if defined(__clang__) || defined(__EDG__) // TRANSITION, VSO-938163
STATIC_ASSERT((test_contiguous<test_vector, bytes<1>>(), true));
#endif // TRANSITION, VSO-938163
test_contiguous<test_vector, bytes<1>>();

#if defined(__clang__) || defined(__EDG__) // TRANSITION, VSO-938163
STATIC_ASSERT((test_contiguous<test_vector, bytes<2>>(), true));
#endif // TRANSITION, VSO-938163
test_contiguous<test_vector, bytes<2>>();

#if defined(__clang__) || defined(__EDG__) // TRANSITION, VSO-938163
STATIC_ASSERT((test_contiguous<test_vector, bytes<4>>(), true));
#endif // TRANSITION, VSO-938163
test_contiguous<test_vector, bytes<4>>();

#if defined(__clang__) || defined(__EDG__) // TRANSITION, VSO-938163
STATIC_ASSERT((test_contiguous<test_vector, bytes<8>>(), true));
#endif // TRANSITION, VSO-938163
test_contiguous<test_vector, bytes<8>>();

#if defined(__clang__) || defined(__EDG__) // TRANSITION, VSO-938163
STATIC_ASSERT((test_contiguous<test_vector, bytes<3>>(), true));
#endif // TRANSITION, VSO-938163
test_contiguous<test_vector, bytes<3>>();
}