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

Implement ranges::reverse_copy #1095

Merged
merged 6 commits into from
Aug 5, 2020
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
91 changes: 90 additions & 1 deletion stl/inc/algorithm
Original file line number Diff line number Diff line change
Expand Up @@ -4927,7 +4927,7 @@ _CONSTEXPR20 _OutIt reverse_copy(_BidIt _First, _BidIt _Last, _OutIt _Dest) {
#if _HAS_IF_CONSTEXPR && _USE_STD_VECTOR_ALGORITHMS
using _Elem = remove_pointer_t<decltype(_UFirst)>;
using _DestElem = remove_pointer_t<decltype(_UDest)>;
constexpr bool _Allow_vectorization = conjunction_v<is_same<remove_const_t<_Elem>, remove_const_t<_DestElem>>,
constexpr bool _Allow_vectorization = conjunction_v<is_same<remove_const_t<_Elem>, _DestElem>,
is_pointer<decltype(_UFirst)>, is_trivially_copyable<_Elem>, negation<is_volatile<_Elem>>>;
constexpr size_t _Nx = sizeof(_Elem);

Expand Down Expand Up @@ -4970,6 +4970,95 @@ _FwdIt reverse_copy(_ExPo&&, _BidIt _First, _BidIt _Last, _FwdIt _Dest) noexcept
_REQUIRE_PARALLEL_ITERATOR(_FwdIt);
return _STD reverse_copy(_First, _Last, _Dest);
}

#ifdef __cpp_lib_concepts
namespace ranges {
// ALIAS TEMPLATE reverse_copy_result
template <class _In, class _Out>
using reverse_copy_result = in_out_result<_In, _Out>;

// VARIABLE ranges::reverse_copy
class _Reverse_copy_fn : private _Not_quite_object {
public:
using _Not_quite_object::_Not_quite_object;

// clang-format off
template <bidirectional_iterator _It, sentinel_for<_It> _Se, weakly_incrementable _Out>
requires indirectly_copyable<_It, _Out>
constexpr reverse_copy_result<_It, _Out> operator()(_It _First, _Se _Last, _Out _Result) const {
// clang-format on
_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);
_Result = _Reverse_copy_common(_STD move(_UFirst), _STD move(_ULast), _STD move(_Result));
return {_STD move(_First), _STD move(_Result)};
}

// clang-format off
template <bidirectional_range _Rng, weakly_incrementable _Out>
requires indirectly_copyable<iterator_t<_Rng>, _Out>
constexpr reverse_copy_result<borrowed_iterator_t<_Rng>, _Out> operator()(_Rng&& _Range, _Out _Result) const {
// clang-format on
if constexpr (common_range<_Rng>) {
_Result = _Reverse_copy_common(_Ubegin(_Range), _Uend(_Range), _STD move(_Result));
return {_RANGES end(_Range), _STD move(_Result)};
} else {
auto _ULast = _Get_final_iterator_unwrapped(_Range);
_Result = _Reverse_copy_common(_Ubegin(_Range), _ULast, _STD move(_Result));
return {_Rewrap_iterator(_Range, _STD move(_ULast)), _STD move(_Result)};
}
}

private:
template <class _It, class _Out>
_NODISCARD static constexpr _Out _Reverse_copy_common(const _It _First, _It _Last, _Out _Result) {
mnatsuhara marked this conversation as resolved.
Show resolved Hide resolved
_STL_INTERNAL_STATIC_ASSERT(bidirectional_iterator<_It>);
_STL_INTERNAL_STATIC_ASSERT(weakly_incrementable<_Out>);
_STL_INTERNAL_STATIC_ASSERT(indirectly_copyable<_It, _Out>);
CaseyCarter marked this conversation as resolved.
Show resolved Hide resolved

#if _USE_STD_VECTOR_ALGORITHMS
if constexpr (contiguous_iterator<_It> && contiguous_iterator<_Out>) {
using _Elem = remove_reference_t<iter_reference_t<_It>>;
using _DestElem = remove_reference_t<iter_reference_t<_Out>>;
constexpr bool _Allow_vectorization = conjunction_v<is_same<remove_const_t<_Elem>, _DestElem>,
is_trivially_copyable<_Elem>, negation<is_volatile<_Elem>>>;
constexpr size_t _Nx = sizeof(_Elem);

#pragma warning(suppress : 6326) // Potential comparison of a constant with another constant
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);
_DestElem* const _Result_addr = _STD to_address(_Result);
if constexpr (_Nx == 1) {
__std_reverse_copy_trivially_copyable_1(_First_addr, _Last_addr, _Result_addr);
} else if constexpr (_Nx == 2) {
__std_reverse_copy_trivially_copyable_2(_First_addr, _Last_addr, _Result_addr);
} else if constexpr (_Nx == 4) {
__std_reverse_copy_trivially_copyable_4(_First_addr, _Last_addr, _Result_addr);
} else {
__std_reverse_copy_trivially_copyable_8(_First_addr, _Last_addr, _Result_addr);
}

_Result += _Last - _First;
return _Result;
}
}
}
#endif // _USE_STD_VECTOR_ALGORITHMS

for (; _First != _Last; ++_Result) {
*_Result = *--_Last;
}

return _Result;
}
};

inline constexpr _Reverse_copy_fn reverse_copy{_Not_quite_object::_Construct_tag{}};
} // namespace ranges
#endif // __cpp_lib_concepts
#endif // _HAS_CXX17

#ifdef __cpp_lib_concepts
Expand Down
10 changes: 10 additions & 0 deletions tests/std/include/range_algorithm_support.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -1063,6 +1063,16 @@ constexpr void test_fwd_write() {
with_forward_ranges<with_writable_iterators<Instantiator, Element2>, Element1>::call();
}

template <class Instantiator, class Element1, class Element2>
constexpr void test_bidi_write() {
with_bidirectional_ranges<with_writable_iterators<Instantiator, Element2>, Element1>::call();
}

template <class Instantiator, class Element1, class Element2>
constexpr void test_contiguous_write() {
with_contiguous_ranges<with_writable_iterators<Instantiator, Element2>, Element1>::call();
}

template <class Instantiator, class Element>
constexpr void test_read() {
with_input_iterators<Instantiator, Element>::call();
Expand Down
3 changes: 2 additions & 1 deletion tests/std/test.lst
Original file line number Diff line number Diff line change
Expand Up @@ -238,7 +238,6 @@ tests\P0769R2_shift_left_shift_right
tests\P0784R7_library_support_for_more_constexpr_containers
tests\P0811R3_midpoint_lerp
tests\P0896R4_P1614R2_comparisons
tests\P0896R4_ranges_algorithm_machinery
tests\P0896R4_ranges_alg_adjacent_find
tests\P0896R4_ranges_alg_all_of
tests\P0896R4_ranges_alg_any_of
Expand Down Expand Up @@ -282,6 +281,7 @@ tests\P0896R4_ranges_alg_replace_copy
tests\P0896R4_ranges_alg_replace_copy_if
tests\P0896R4_ranges_alg_replace_if
tests\P0896R4_ranges_alg_reverse
tests\P0896R4_ranges_alg_reverse_copy
tests\P0896R4_ranges_alg_rotate
tests\P0896R4_ranges_alg_rotate_copy
tests\P0896R4_ranges_alg_sample
Expand All @@ -297,6 +297,7 @@ tests\P0896R4_ranges_alg_transform_binary
tests\P0896R4_ranges_alg_transform_unary
tests\P0896R4_ranges_alg_unique
tests\P0896R4_ranges_alg_unique_copy
tests\P0896R4_ranges_algorithm_machinery
tests\P0896R4_ranges_iterator_machinery
tests\P0896R4_ranges_range_machinery
tests\P0896R4_ranges_subrange
Expand Down
4 changes: 4 additions & 0 deletions tests/std/tests/P0896R4_ranges_alg_reverse_copy/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
163 changes: 163 additions & 0 deletions tests/std/tests/P0896R4_ranges_alg_reverse_copy/test.cpp
Original file line number Diff line number Diff line change
@@ -0,0 +1,163 @@
// 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 that reverse_copy_result aliases in_out_result
STATIC_ASSERT(same_as<ranges::reverse_copy_result<int, double>, ranges::in_out_result<int, double>>);

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

struct nontrivial_int {
int val;

constexpr nontrivial_int(int i = 0) 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 input[] = {13, 42, 1367};
static constexpr nontrivial_int expected[] = {1367, 42, 13};

template <ranges::bidirectional_range In, indirectly_writable<ranges::range_reference_t<In>> Out>
static constexpr void call() {
if constexpr (!ranges::contiguous_range<In>) { // the vectorized tests below have plenty of contiguous coverage
using ranges::reverse_copy, ranges::reverse_copy_result, ranges::begin, ranges::end, ranges::equal,
ranges::iterator_t;

{ // Validate iterator overload
nontrivial_int output[3];
In wrapped_input{input};
const same_as<reverse_copy_result<iterator_t<In>, Out>> auto result =
reverse_copy(wrapped_input.begin(), wrapped_input.end(), Out{output});
assert(result.in == wrapped_input.end());
assert(result.out.peek() == end(output));
assert(equal(output, expected));
}
{ // Validate range overload
nontrivial_int output[3];
In wrapped_input{input};
const same_as<reverse_copy_result<iterator_t<In>, Out>> auto result =
reverse_copy(wrapped_input, Out{output});
assert(result.in == wrapped_input.end());
assert(result.out.peek() == end(output));
assert(equal(output, expected));
}

{ // Validate iterator overload, empty range
nontrivial_int output[3];
In wrapped_input{};
const same_as<reverse_copy_result<iterator_t<In>, Out>> auto result =
reverse_copy(wrapped_input.begin(), wrapped_input.end(), Out{output});
assert(result.in == wrapped_input.end());
assert(result.out.peek() == begin(output));
}
{ // Validate range overload, empty range
nontrivial_int output[3];
In wrapped_input{};
const same_as<reverse_copy_result<iterator_t<In>, Out>> auto result =
reverse_copy(wrapped_input, Out{output});
assert(result.in == wrapped_input.end());
assert(result.out.peek() == begin(output));
}
}
}
};

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

constexpr bytes() {
ranges::fill(storage, static_cast<unsigned char>(-1));
}

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

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

struct test_vector {
template <ranges::contiguous_range In, indirectly_writable<ranges::range_reference_t<In>> Out>
static constexpr void call() {
using ranges::reverse_copy, ranges::reverse_copy_result, ranges::begin, ranges::end, ranges::equal,
ranges::iterator_t, ranges::range_value_t;

const range_value_t<In> input[3] = {0x10, 0x20, 0x30};
const range_value_t<In> expected[3] = {0x30, 0x20, 0x10};

{ // Validate iterator overload, vectorizable
range_value_t<In> output[3];
In wrapped_input{input};
const same_as<reverse_copy_result<iterator_t<In>, Out>> auto result =
reverse_copy(wrapped_input.begin(), wrapped_input.end(), Out{output});
assert(result.in == wrapped_input.end());
assert(result.out.peek() == end(output));
assert(equal(output, expected));
}
{ // Validate range overload, vectorizable
range_value_t<In> output[3];
In wrapped_input{input};
const same_as<reverse_copy_result<iterator_t<In>, Out>> auto result =
reverse_copy(wrapped_input, Out{output});
assert(result.in == wrapped_input.end());
assert(result.out.peek() == end(output));
assert(equal(output, expected));
}

{ // Validate iterator overload, vectorizable empty
range_value_t<In> output[3];
In wrapped_input{};
const same_as<reverse_copy_result<iterator_t<In>, Out>> auto result =
reverse_copy(wrapped_input.begin(), wrapped_input.end(), Out{output});
assert(result.in == wrapped_input.end());
assert(result.out.peek() == begin(output));
}
{ // Validate range overload, vectorizable empty
range_value_t<In> output[3];
In wrapped_input{};
const same_as<reverse_copy_result<iterator_t<In>, Out>> auto result =
reverse_copy(wrapped_input, Out{output});
assert(result.in == wrapped_input.end());
assert(result.out.peek() == begin(output));
}
}
};

int main() {
STATIC_ASSERT((test_bidi_write<instantiator, const nontrivial_int, nontrivial_int>(), true));
test_bidi_write<instantiator, const nontrivial_int, nontrivial_int>();

STATIC_ASSERT((test_contiguous_write<test_vector, const bytes<1>, bytes<1>>(), true));
STATIC_ASSERT((test_contiguous_write<test_vector, const bytes<2>, bytes<2>>(), true));
STATIC_ASSERT((test_contiguous_write<test_vector, const bytes<4>, bytes<4>>(), true));
STATIC_ASSERT((test_contiguous_write<test_vector, const bytes<8>, bytes<8>>(), true));
STATIC_ASSERT((test_contiguous_write<test_vector, const bytes<3>, bytes<3>>(), true));
test_contiguous_write<test_vector, const bytes<1>, bytes<1>>();
test_contiguous_write<test_vector, const bytes<2>, bytes<2>>();
test_contiguous_write<test_vector, const bytes<4>, bytes<4>>();
test_contiguous_write<test_vector, const bytes<8>, bytes<8>>();
test_contiguous_write<test_vector, const bytes<3>, bytes<3>>();
}