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 P2408R5: Ranges Iterators As Inputs To Non-Ranges Algorithms #2960

Merged
merged 18 commits into from
Aug 3, 2022
Merged
Show file tree
Hide file tree
Changes from 8 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
1 change: 1 addition & 0 deletions stl/inc/algorithm
Original file line number Diff line number Diff line change
Expand Up @@ -4408,6 +4408,7 @@ template <class _ExPo, class _BidIt, class _FwdIt, _Enable_if_execution_policy_t
_FwdIt reverse_copy(_ExPo&&, _BidIt _First, _BidIt _Last, _FwdIt _Dest) noexcept /* terminates */ {
// copy reversing elements in [_First, _Last)
// not parallelized as benchmarks show it isn't worth it
_REQUIRE_CONST_PARALLEL_ITERATOR(_BidIt);
_REQUIRE_MUTABLE_PARALLEL_ITERATOR(_FwdIt);
return _STD reverse_copy(_First, _Last, _Dest);
}
Expand Down
3 changes: 2 additions & 1 deletion stl/inc/execution
Original file line number Diff line number Diff line change
Expand Up @@ -1577,6 +1577,7 @@ _NODISCARD _FwdIt1 find_first_of(_ExPo&& _Exec, const _FwdIt1 _First1, _FwdIt1 _
const _FwdIt2 _Last2, _Pr _Pred) noexcept /* terminates */ {
// look for one of [_First2, _Last2) that matches element
_REQUIRE_CONST_PARALLEL_ITERATOR(_FwdIt1);
_REQUIRE_CONST_PARALLEL_ITERATOR(_FwdIt2);
using _UFwdIt1 = _Unwrapped_t<const _FwdIt1&>;
_Adl_verify_range(_First1, _Last1);
_Adl_verify_range(_First2, _Last2);
Expand Down Expand Up @@ -4389,7 +4390,7 @@ _FwdIt2 exclusive_scan(_ExPo&&, const _FwdIt1 _First, const _FwdIt1 _Last, _FwdI
_BinOp _Reduce_op) noexcept /* terminates */ {
// set each value in [_Dest, _Dest + (_Last - _First)) to the associative reduction of predecessors and _Val
_REQUIRE_CONST_PARALLEL_ITERATOR(_FwdIt1);
_REQUIRE_CONST_PARALLEL_ITERATOR(_FwdIt2);
_REQUIRE_MUTABLE_PARALLEL_ITERATOR(_FwdIt2);
_Adl_verify_range(_First, _Last);
const auto _UFirst = _Get_unwrapped(_First);
const auto _ULast = _Get_unwrapped(_Last);
Expand Down
4 changes: 2 additions & 2 deletions stl/inc/vector
Original file line number Diff line number Diff line change
Expand Up @@ -694,7 +694,7 @@ public:
_Adl_verify_range(_First, _Last);
auto _UFirst = _Get_unwrapped(_First);
auto _ULast = _Get_unwrapped(_Last);
if constexpr (_Is_ranges_fwd_iter_v<_Iter>) {
if constexpr (_Is_fwd_iter_v<_Iter>) {
const auto _Count = _Convert_size<size_type>(static_cast<size_t>(_STD distance(_UFirst, _ULast)));
_Construct_n(_Count, _STD move(_UFirst), _STD move(_ULast));
#ifdef __cpp_lib_concepts
Expand Down Expand Up @@ -3135,7 +3135,7 @@ public:
_CONSTEXPR20 iterator insert(const_iterator _Where, _Iter _First, _Iter _Last) {
const difference_type _Saved_offset = _Where - begin();

if constexpr (_Is_ranges_fwd_iter_v<_Iter>) {
if constexpr (_Is_fwd_iter_v<_Iter>) {
_Adl_verify_range(_First, _Last);
const auto _Count = _Convert_size<size_type>(static_cast<size_t>(_STD distance(_First, _Last)));
const size_type _Off = _Insert_x(_Where, _Count);
Expand Down
4 changes: 2 additions & 2 deletions stl/inc/xutility
Original file line number Diff line number Diff line change
Expand Up @@ -853,7 +853,7 @@ _INLINE_VAR constexpr bool _Is_ranges_fwd_iter_v =
#if defined(__cpp_lib_concepts)
forward_iterator<_Iter> ||
#endif
is_convertible_v<_Iter_cat_t<_Iter>, forward_iterator_tag>;
_Is_fwd_iter_v<_Iter>;

template <class _Iter>
_INLINE_VAR constexpr bool _Is_bidi_iter_v = is_convertible_v<_Iter_cat_t<_Iter>, bidirectional_iterator_tag>;
Expand All @@ -863,7 +863,7 @@ _INLINE_VAR constexpr bool _Is_ranges_bidi_iter_v =
#if defined(__cpp_lib_concepts)
bidirectional_iterator<_Iter> ||
#endif
is_convertible_v<_Iter_cat_t<_Iter>, bidirectional_iterator_tag>;
_Is_bidi_iter_v<_Iter>;

template <class _Iter>
_INLINE_VAR constexpr bool _Is_random_iter_v = is_convertible_v<_Iter_cat_t<_Iter>, random_access_iterator_tag>;
Expand Down
1 change: 1 addition & 0 deletions stl/inc/yvals_core.h
Original file line number Diff line number Diff line change
Expand Up @@ -270,6 +270,7 @@
// P2367R0 Remove Misuses Of List-Initialization From Clause 24 Ranges
// P2372R3 Fixing Locale Handling In chrono Formatters
// P2393R1 Cleaning Up Integer-Class Types
// P2408R5 Ranges Iterators As Inputs To Non-Ranges Algorithms
// P2415R2 What Is A view?
// P2418R2 Add Support For std::generator-like Types To std::format
// P2432R1 Fix istream_view
Expand Down
1 change: 1 addition & 0 deletions tests/std/test.lst
Original file line number Diff line number Diff line change
Expand Up @@ -488,6 +488,7 @@ tests\P2231R1_complete_constexpr_optional_variant
tests\P2273R3_constexpr_unique_ptr
tests\P2321R2_proxy_reference
tests\P2401R0_conditional_noexcept_for_exchange
tests\P2408R5_ranges_iterators_to_classic_algorithms
tests\P2415R2_owning_view
tests\P2440R1_ranges_alg_shift_left
tests\P2440R1_ranges_alg_shift_right
Expand Down
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 ..\strict_concepts_20_matrix.lst
Original file line number Diff line number Diff line change
@@ -0,0 +1,190 @@
// Copyright (c) Microsoft Corporation.
// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception

#include <algorithm>
#include <array>
#include <execution>
#include <initializer_list>
#include <ranges>

#include <range_algorithm_support.hpp>

using namespace std;

template <class T, class U>
using second = U;
template <class, class Input>
Input second_v(Input x) {
return x;
}

template <class I>
struct iterator_adaptor {
std::vector<int> v;
iterator_adaptor(initializer_list<int> il) : v(il) {}
iterator_adaptor(size_t n) : v(n) {}

using iterator = I;
using const_iterator = typename I::Consterator;

iterator begin() {
return iterator{v.data()};
}
iterator end() {
return iterator{v.data() + v.size()};
}

const_iterator cbegin() const {
return const_iterator{v.data()};
}
const_iterator cend() const {
return const_iterator{v.data() + v.size()};
}
};

template <class... Is>
struct helper {
helper(second<Is, initializer_list<int>>... ils) : tup(ils...) {}

helper(initializer_list<int> il) requires(sizeof...(Is) > 1) : tup(second_v<Is>(il.size())...) {
get<0>(tup).v.assign(il);
}

std::tuple<iterator_adaptor<Is>...> tup;
};

template <size_t Idx, class... Is>
auto hbegin(helper<Is...>& h) {
return get<Idx>(h.tup).begin();
}
template <size_t Idx, class... Is>
auto hend(helper<Is...>& h) {
return get<Idx>(h.tup).end();
}

template <size_t Idx, class... Is>
auto hcbegin(const helper<Is...>& h) {
return get<Idx>(h.tup).cbegin();
}
template <size_t Idx, class... Is>
auto hcend(const helper<Is...>& h) {
return get<Idx>(h.tup).cend();
}

template <class It>
struct unary_algorithms {
static void call() {
if constexpr (forward_iterator<It>) {
// parallel algorithms
using execution::seq;
{
helper<It> h({1, 2, 3, 4, 5, 6, 7, 8, 9, 10});
auto res = reduce(seq, hcbegin<0>(h), hcend<0>(h), 0);
assert(res == 55);
}
}
}
};

template <class I1, class I2>
struct binary_algorithms {
static void call() {
if constexpr (forward_iterator<I1> || _Is_fwd_iter_v<I2>) {
helper<I1, I2> h({0, 0, 1, 2, 3, 3, 4, 5});
array exp{0, 1, 2, 3, 4, 5};
auto it = unique_copy(hcbegin<0>(h), hcend<0>(h), hbegin<1>(h));
assert(equal(hbegin<1>(h), it, exp.begin(), exp.end()));
}

if constexpr (forward_iterator<I1> && forward_iterator<I2>) {
{
helper<I1, I2> h{{0, 1, 2, 3, 4, 5}, {5, 4, 3, 2, 1, 0}};
assert(is_permutation(hcbegin<0>(h), hcend<0>(h), hcbegin<1>(h), hcend<1>(h)));
}

// parallel algorithms
using execution::seq;
{
helper<I1, I2> h{{0, 1, 2, 3, 4, 5}, {0, 1, 3, 4, 5}};
auto pr = mismatch(seq, hcbegin<0>(h), hcend<0>(h), hcbegin<1>(h), hcend<1>(h));
assert(distance(hcbegin<0>(h), pr.first) == 2);
assert(*pr.first == 2);
assert(*pr.second == 3);
}
if constexpr (_Is_fwd_iter_v<I2>) {
helper<I1, I2> h{{0, 1, 2, 3, 4, 5}};
array expected{0, 1, 2, 4, 5};
auto it = copy_if(seq, hcbegin<0>(h), hcend<0>(h), hbegin<1>(h), [](int x) { return x != 3; });
assert(equal(hbegin<1>(h), it, expected.begin(), expected.end()));
}
}
}
};

template <class I1, class I2, class I3>
struct ternary_algorithms {
static void call() {
if constexpr (forward_iterator<I1> && forward_iterator<I2> && forward_iterator<I3>) {
// parallel algorithms
using execution::seq;
if constexpr (_Is_fwd_iter_v<I2> && _Is_fwd_iter_v<I3>) {
helper<I1, I2, I3> h{{0, 1, 2, 3, 4, 5}};
array exp1{0, 1};
array exp2{2, 3, 4, 5};
auto pr = partition_copy(
seq, hcbegin<0>(h), hcend<0>(h), hbegin<1>(h), hbegin<2>(h), [](int x) { return x < 2; });
assert(equal(hbegin<1>(h), pr.first, exp1.begin(), exp1.end()));
assert(equal(hbegin<2>(h), pr.second, exp2.begin(), exp2.end()));
}
}
}
};

using input_iter = test::iterator<input_iterator_tag, int, test::CanDifference::no, test::CanCompare::yes>;
using fwd_iter = test::iterator<forward_iterator_tag, int>;
using bidi_iter = test::iterator<bidirectional_iterator_tag, int>;
using random_iter = test::iterator<random_access_iterator_tag, int>;
using cpp17_fwd_iter =
test::iterator<forward_iterator_tag, int, test::CanDifference::no, test::CanCompare::yes, test::ProxyRef::no>;
using cpp17_bidi_iter =
test::iterator<bidirectional_iterator_tag, int, test::CanDifference::no, test::CanCompare::yes, test::ProxyRef::no>;
using cpp17_random_iter = test::iterator<random_access_iterator_tag, int, test::CanDifference::yes,
test::CanCompare::yes, test::ProxyRef::no>;

// Sanity checks
static_assert(!_Is_fwd_iter_v<input_iter> && !forward_iterator<input_iter>);
static_assert(!_Is_fwd_iter_v<fwd_iter> && forward_iterator<fwd_iter>);
static_assert(!_Is_fwd_iter_v<bidi_iter> && bidirectional_iterator<bidi_iter>);
static_assert(!_Is_fwd_iter_v<random_iter> && random_access_iterator<random_iter>);
static_assert(_Is_fwd_iter_v<cpp17_fwd_iter> && forward_iterator<cpp17_fwd_iter>);
static_assert(_Is_bidi_iter_v<cpp17_bidi_iter> && bidirectional_iterator<cpp17_bidi_iter>);
static_assert(_Is_random_iter_v<cpp17_random_iter> && random_access_iterator<cpp17_random_iter>);

template <template <class...> class C>
struct instantiator {
template <class... Its>
struct curry_t {
using curry = instantiator<curry_t>;

static void call() {
C<input_iter, Its...>::call();
C<fwd_iter, Its...>::call();
C<bidi_iter, Its...>::call();
C<random_iter, Its...>::call();
C<cpp17_fwd_iter, Its...>::call();
C<cpp17_bidi_iter, Its...>::call();
C<cpp17_random_iter, Its...>::call();
}
};
using curry = typename curry_t<>::curry;

static void call() {
curry_t<>::call();
}
};

int main() {
instantiator<unary_algorithms>::call();
instantiator<binary_algorithms>::curry::call();
instantiator<ternary_algorithms>::curry::curry::call();
}
Original file line number Diff line number Diff line change
Expand Up @@ -30,6 +30,16 @@ STATIC_ASSERT(__cpp_lib_adaptor_iterator_pair_constructor == 202106L);
STATIC_ASSERT(__cpp_lib_addressof_constexpr == 201603L);
#endif

#if _HAS_CXX20 && defined(__cpp_lib_concepts)
#ifndef __cpp_lib_algorithm_iterator_requirements
#error __cpp_lib_algorithm_iterator_requirements is not defined
#elif __cpp_lib_algorithm_iterator_requirements != 202207L
#error __cpp_lib_algorithm_iterator_requirements is not 202207L
#else
STATIC_ASSERT(__cpp_lib_algorithm_iterator_requirements == 202207L);
#endif
#endif

#if _HAS_CXX23 && !defined(__EDG__) // TRANSITION, EDG concepts support
#ifndef __cpp_lib_allocate_at_least
#error __cpp_lib_allocate_at_least is not defined
Expand Down