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
114 changes: 114 additions & 0 deletions stl/inc/algorithm
Original file line number Diff line number Diff line change
Expand Up @@ -3266,6 +3266,120 @@ _FwdIt3 transform(_ExPo&& _Exec, const _FwdIt1 _First1, const _FwdIt1 _Last1, co
_Fn _Func) noexcept; // terminates
#endif // _HAS_CXX17

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

// ALIAS TEMPLATE binary_transform_result
template <class _In1, class _In2, class _Out>
using binary_transform_result = in_in_out_result<_In1, _In2, _Out>;

// VARIABLE ranges::transform
class _Transform_fn : private _Not_quite_object {
public:
using _Not_quite_object::_Not_quite_object;

// clang-format off
template <input_iterator _It, sentinel_for<_It> _Se, weakly_incrementable _Out, copy_constructible _Fn,
class _Pj = identity>
requires indirectly_writable<_Out, indirect_result_t<_Fn&, projected<_It, _Pj>>>
constexpr unary_transform_result<_It, _Out> operator()(
_It _First, _Se _Last, _Out _Result, _Fn _Func, _Pj _Proj = {}) const {
_Adl_verify_range(_First, _Last);
auto _UResult = _Transform_unary_unchecked(_Get_unwrapped(_STD move(_First)),
_Get_unwrapped(_STD move(_Last)), _STD move(_Result), _Pass_fn(_Func), _Pass_fn(_Proj));

_Seek_wrapped(_First, _STD move(_UResult.in));
return {_STD move(_First), _STD move(_UResult.out)};
}

template <input_range _Rng, weakly_incrementable _Out, copy_constructible _Fn, class _Pj = identity>
requires indirectly_writable<_Out, indirect_result_t<_Fn&, projected<iterator_t<_Rng>, _Pj>>>
constexpr unary_transform_result<borrowed_iterator_t<_Rng>, _Out> operator()(
_Rng&& _Range, _Out _Result, _Fn _Func, _Pj _Proj = {}) const {
auto _First = _RANGES begin(_Range);
auto _UResult = _Transform_unary_unchecked(
_Get_unwrapped(_STD move(_First)), _Uend(_Range), _STD move(_Result), _Pass_fn(_Func), _Pass_fn(_Proj));

_Seek_wrapped(_First, _STD move(_UResult.in));
return {_STD move(_First), _STD move(_UResult.out)};
}

template <input_iterator _It1, sentinel_for<_It1> _Se1, input_iterator _It2, sentinel_for<_It2> _Se2,
weakly_incrementable _Out, copy_constructible _Fn, class _Pj1 = identity, class _Pj2 = identity>
requires indirectly_writable<_Out, indirect_result_t<_Fn&, projected<_It1, _Pj1>, projected<_It2, _Pj2>>>
constexpr binary_transform_result<_It1, _It2, _Out> operator()(_It1 _First1, _Se1 _Last1, _It2 _First2,
_Se2 _Last2, _Out _Result, _Fn _Func, _Pj1 _Proj1 = {}, _Pj2 _Proj2 = {}) const {
_Adl_verify_range(_First1, _Last1);
_Adl_verify_range(_First2, _Last2);
auto _UResult =
_Transform_binary_unchecked(_Get_unwrapped(_STD move(_First1)), _Get_unwrapped(_STD move(_Last1)),
_Get_unwrapped(_STD move(_First2)), _Get_unwrapped(_STD move(_Last2)), _STD move(_Result),
_Pass_fn(_Func), _Pass_fn(_Proj1), _Pass_fn(_Proj2));

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

template <input_range _Rng1, input_range _Rng2, weakly_incrementable _Out, copy_constructible _Fn,
class _Pj1 = identity, class _Pj2 = identity>
requires indirectly_writable<_Out, indirect_result_t<_Fn&, projected<iterator_t<_Rng1>, _Pj1>,
projected<iterator_t<_Rng2>, _Pj2>>>
constexpr binary_transform_result<borrowed_iterator_t<_Rng1>, borrowed_iterator_t<_Rng2>, _Out> operator()(
_Rng1&& _Range1, _Rng2&& _Range2, _Out _Result, _Fn _Func, _Pj1 _Proj1 = {}, _Pj2 _Proj2 = {}) const {
auto _First1 = _RANGES begin(_Range1);
auto _First2 = _RANGES begin(_Range2);
auto _UResult = _Transform_binary_unchecked(_Get_unwrapped(_STD move(_First1)), _Uend(_Range1),
_Get_unwrapped(_STD move(_First2)), _Uend(_Range2), _STD move(_Result), _Pass_fn(_Func),
_Pass_fn(_Proj1), _Pass_fn(_Proj2));

_Seek_wrapped(_First1, _STD move(_UResult.in1));
_Seek_wrapped(_First2, _STD move(_UResult.in2));
return {_STD move(_First1), _STD move(_First2), _STD move(_UResult.out)};
}
// clang-format on

private:
template <class _It, class _Se, class _Out, class _Fn, class _Pj>
_NODISCARD static constexpr unary_transform_result<_It, _Out> _Transform_unary_unchecked(
_It _First, const _Se _Last, _Out _Result, _Fn _Func, _Pj _Proj) {
// transform projected [_First, _Last) with _Func
_STL_INTERNAL_STATIC_ASSERT(input_iterator<_It>);
_STL_INTERNAL_STATIC_ASSERT(weakly_incrementable<_Out>);
_STL_INTERNAL_STATIC_ASSERT(indirectly_writable<_Out, indirect_result_t<_Fn&, projected<_It, _Pj>>>);

for (; _First != _Last; ++_First, (void) ++_Result) {
*_Result = _STD invoke(_Func, _STD invoke(_Proj, *_First));
}

return {_STD move(_First), _STD move(_Result)};
}

template <class _It1, class _Se1, class _It2, class _Se2, class _Out, class _Fn, class _Pj1, class _Pj2>
_NODISCARD static constexpr binary_transform_result<_It1, _It2, _Out> _Transform_binary_unchecked(_It1 _First1,
const _Se1 _Last1, _It2 _First2, const _Se2 _Last2, _Out _Result, _Fn _Func, _Pj1 _Proj1, _Pj2 _Proj2) {
// transform projected [_First1, _Last1) and projected [_First2, _Last2) with _Func
_STL_INTERNAL_STATIC_ASSERT(input_iterator<_It1>);
_STL_INTERNAL_STATIC_ASSERT(input_iterator<_It2>);
_STL_INTERNAL_STATIC_ASSERT(weakly_incrementable<_Out>);
_STL_INTERNAL_STATIC_ASSERT(
indirectly_writable<_Out, indirect_result_t<_Fn&, projected<_It1, _Pj1>, projected<_It2, _Pj2>>>);

for (; _First1 != _Last1 && _First2 != _Last2; ++_First1, (void) ++_First2, ++_Result) {
*_Result = _STD invoke(_Func, _STD invoke(_Proj1, *_First1), _STD invoke(_Proj2, *_First2));
}

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

inline constexpr _Transform_fn transform{_Not_quite_object::_Construct_tag{}};
} // namespace ranges
#endif // __cpp_lib_concepts

// FUNCTION TEMPLATE replace
template <class _FwdIt, class _Ty>
_CONSTEXPR20 void replace(const _FwdIt _First, const _FwdIt _Last, const _Ty& _Oldval, const _Ty& _Newval) {
Expand Down
5 changes: 5 additions & 0 deletions tests/std/include/range_algorithm_support.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -1018,6 +1018,11 @@ constexpr void test_read_write() {
with_input_iterators<with_writable_iterators<Instantiator, Element2>, Element1>::call();
}

template <class Instantiator, class Element1, class Element2, class Element3>
constexpr void test_in_in_write() {
with_input_ranges<with_input_ranges<with_writable_iterators<Instantiator, Element3>, Element2>, Element1>::call();
}

template <size_t I>
struct get_nth_fn {
template <class T>
Expand Down
2 changes: 2 additions & 0 deletions tests/std/test.lst
Original file line number Diff line number Diff line change
Expand Up @@ -273,6 +273,8 @@ 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_transform_binary
tests\P0896R4_ranges_alg_transform_unary
tests\P0896R4_ranges_iterator_machinery
tests\P0896R4_ranges_range_machinery
tests\P0896R4_ranges_subrange
Expand Down
5 changes: 2 additions & 3 deletions tests/std/tests/P0896R4_ranges_alg_copy/test.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -15,10 +15,9 @@ using namespace std;
STATIC_ASSERT(same_as<ranges::copy_result<int, double>, ranges::in_out_result<int, double>>);

// Validate dangling story
STATIC_ASSERT(same_as<decltype(ranges::copy(borrowed<false>{}, static_cast<int*>(nullptr))),
ranges::copy_result<ranges::dangling, int*>>);
STATIC_ASSERT(
same_as<decltype(ranges::copy(borrowed<true>{}, static_cast<int*>(nullptr))), ranges::copy_result<int*, int*>>);
same_as<decltype(ranges::copy(borrowed<false>{}, nullptr_to<int>)), ranges::copy_result<ranges::dangling, int*>>);
STATIC_ASSERT(same_as<decltype(ranges::copy(borrowed<true>{}, nullptr_to<int>)), ranges::copy_result<int*, int*>>);

struct instantiator {
static constexpr int input[3] = {13, 42, 1729};
Expand Down
6 changes: 3 additions & 3 deletions tests/std/tests/P0896R4_ranges_alg_copy_if/test.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -65,10 +65,10 @@ constexpr auto is_odd = [](int const x) { return x % 2 != 0; };
STATIC_ASSERT(same_as<ranges::copy_if_result<int, double>, ranges::in_out_result<int, double>>);

// Validate dangling story
STATIC_ASSERT(same_as<decltype(ranges::copy_if(borrowed<false>{}, static_cast<int*>(nullptr), is_odd)),
STATIC_ASSERT(same_as<decltype(ranges::copy_if(borrowed<false>{}, nullptr_to<int>, is_odd)),
ranges::copy_if_result<ranges::dangling, int*>>);
STATIC_ASSERT(same_as<decltype(ranges::copy_if(borrowed<true>{}, static_cast<int*>(nullptr), is_odd)),
ranges::copy_if_result<int*, int*>>);
STATIC_ASSERT(
same_as<decltype(ranges::copy_if(borrowed<true>{}, nullptr_to<int>, is_odd)), ranges::copy_if_result<int*, int*>>);

struct instantiator {
static constexpr P input[3] = {{1, 99}, {4, 98}, {5, 97}};
Expand Down
5 changes: 2 additions & 3 deletions tests/std/tests/P0896R4_ranges_alg_move/test.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -27,10 +27,9 @@ struct int_wrapper {
STATIC_ASSERT(same_as<ranges::move_result<int, double>, ranges::in_out_result<int, double>>);

// Validate dangling story
STATIC_ASSERT(same_as<decltype(ranges::move(borrowed<false>{}, static_cast<int*>(nullptr))),
ranges::move_result<ranges::dangling, int*>>);
STATIC_ASSERT(
same_as<decltype(ranges::move(borrowed<true>{}, static_cast<int*>(nullptr))), ranges::move_result<int*, int*>>);
same_as<decltype(ranges::move(borrowed<false>{}, nullptr_to<int>)), ranges::move_result<ranges::dangling, int*>>);
STATIC_ASSERT(same_as<decltype(ranges::move(borrowed<true>{}, nullptr_to<int>)), ranges::move_result<int*, int*>>);

struct instantiator {
static constexpr int_wrapper expected_output[3] = {13, 55, 12345};
Expand Down
4 changes: 2 additions & 2 deletions tests/std/tests/P0896R4_ranges_alg_replace_copy/test.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -15,9 +15,9 @@ using P = pair<int, int>;
STATIC_ASSERT(same_as<ranges::replace_copy_result<int, double>, ranges::in_out_result<int, double>>);

// Validate dangling story
STATIC_ASSERT(same_as<decltype(ranges::replace_copy(borrowed<false>{}, static_cast<int*>(nullptr), 42, 5)),
STATIC_ASSERT(same_as<decltype(ranges::replace_copy(borrowed<false>{}, nullptr_to<int>, 42, 5)),
ranges::replace_copy_result<ranges::dangling, int*>>);
STATIC_ASSERT(same_as<decltype(ranges::replace_copy(borrowed<true>{}, static_cast<int*>(nullptr), 42, 5)),
STATIC_ASSERT(same_as<decltype(ranges::replace_copy(borrowed<true>{}, nullptr_to<int>, 42, 5)),
ranges::replace_copy_result<int*, int*>>);

struct instantiator {
Expand Down
4 changes: 2 additions & 2 deletions tests/std/tests/P0896R4_ranges_alg_replace_copy_if/test.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -17,9 +17,9 @@ constexpr auto matches = [](int const val) { return val == 47; };
STATIC_ASSERT(same_as<ranges::replace_copy_if_result<int, double>, ranges::in_out_result<int, double>>);

// Validate dangling story
STATIC_ASSERT(same_as<decltype(ranges::replace_copy_if(borrowed<false>{}, static_cast<int*>(nullptr), matches, 5)),
STATIC_ASSERT(same_as<decltype(ranges::replace_copy_if(borrowed<false>{}, nullptr_to<int>, matches, 5)),
ranges::replace_copy_if_result<ranges::dangling, int*>>);
STATIC_ASSERT(same_as<decltype(ranges::replace_copy_if(borrowed<true>{}, static_cast<int*>(nullptr), matches, 5)),
STATIC_ASSERT(same_as<decltype(ranges::replace_copy_if(borrowed<true>{}, nullptr_to<int>, matches, 5)),
ranges::replace_copy_if_result<int*, int*>>);

struct instantiator {
Expand Down
4 changes: 4 additions & 0 deletions tests/std/tests/P0896R4_ranges_alg_transform_binary/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
128 changes: 128 additions & 0 deletions tests/std/tests/P0896R4_ranges_alg_transform_binary/test.cpp
Original file line number Diff line number Diff line change
@@ -0,0 +1,128 @@
// Copyright (c) Microsoft Corporation.
// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception

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

#include <range_algorithm_support.hpp>

using namespace std;
using P = pair<int, int>;

// Validate that binary_transform_result aliases in_in_out_result
STATIC_ASSERT(same_as<ranges::binary_transform_result<int, P, double>, ranges::in_in_out_result<int, P, double>>);

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

struct instantiator {
static constexpr P input1[3] = {{1, 99}, {4, 98}, {5, 97}};
static constexpr P input2[3] = {{99, 6}, {98, 7}, {97, 8}};
static constexpr int expected[3] = {7, 11, 13};

static constexpr P shortInput1[2] = {{1, 99}, {4, 98}};
static constexpr P shortInput2[2] = {{99, 6}, {98, 7}};
static constexpr int shortExpected[2] = {7, 11};

template <ranges::input_range Read1, ranges::input_range Read2, weakly_incrementable Write>
static constexpr void call() {
using ranges::transform, ranges::binary_transform_result, ranges::iterator_t;
{ // Validate iterator + sentinel overload, first range shorter
int output[2] = {-1, -1};
Read1 wrapped_in1{shortInput1};
Read2 wrapped_in2{input2};

auto result = transform(wrapped_in1.begin(), wrapped_in1.end(), wrapped_in2.begin(), wrapped_in2.end(),
Write{output}, plus{}, get_first, get_second);
STATIC_ASSERT(
same_as<decltype(result), binary_transform_result<iterator_t<Read1>, iterator_t<Read2>, Write>>);
assert(result.in1 == wrapped_in1.end());
assert(next(result.in2) == wrapped_in2.end());
assert(result.out.peek() == output + 2);
assert(ranges::equal(output, shortExpected));
}
{ // Validate iterator + sentinel overload, second range shorter
int output[2] = {-1, -1};
Read1 wrapped_in1{input1};
Read2 wrapped_in2{shortInput2};

auto result = transform(wrapped_in1.begin(), wrapped_in1.end(), wrapped_in2.begin(), wrapped_in2.end(),
Write{output}, plus{}, get_first, get_second);
STATIC_ASSERT(
same_as<decltype(result), binary_transform_result<iterator_t<Read1>, iterator_t<Read2>, Write>>);
assert(next(result.in1) == wrapped_in1.end());
assert(result.in2 == wrapped_in2.end());
assert(result.out.peek() == output + 2);
assert(ranges::equal(output, shortExpected));
}
{ // Validate range overload, first range shorter
int output[2] = {-1, -1};
Read1 wrapped_in1{shortInput1};
Read2 wrapped_in2{input2};

auto result = transform(wrapped_in1, wrapped_in2, Write{output}, plus{}, get_first, get_second);
STATIC_ASSERT(
same_as<decltype(result), binary_transform_result<iterator_t<Read1>, iterator_t<Read2>, Write>>);
assert(result.in1 == wrapped_in1.end());
assert(next(result.in2) == wrapped_in2.end());
assert(result.out.peek() == output + 2);
assert(ranges::equal(output, shortExpected));
}
{ // Validate range overload, second range shorter
int output[2] = {-1, -1};
Read1 wrapped_in1{input1};
Read2 wrapped_in2{shortInput2};

auto result = transform(wrapped_in1, wrapped_in2, Write{output}, plus{}, get_first, get_second);
STATIC_ASSERT(
same_as<decltype(result), binary_transform_result<iterator_t<Read1>, iterator_t<Read2>, Write>>);
assert(next(result.in1) == wrapped_in1.end());
assert(result.in2 == wrapped_in2.end());
assert(result.out.peek() == output + 2);
assert(ranges::equal(output, shortExpected));
}
}
};

using Elem1 = const P;
using Elem2 = const P;
using Elem3 = int;

#ifdef TEST_EVERYTHING
int main() {
// No constexpr test here; the test_in_in_write call exceeds the maximum number of steps in a constexpr computation.
test_in_in_write<instantiator, Elem1, Elem2, Elem3>();
}
#else // ^^^ test all range combinations // test only interesting range combos vvv
template <class Elem, test::Sized IsSized>
using in_test_range = test::range<input_iterator_tag, Elem, IsSized, test::CanDifference::no, test::Common::no,
test::CanCompare::yes, test::ProxyRef::yes>;
template <class Elem>
using out_test_iterator =
test::iterator<output_iterator_tag, Elem, test::CanDifference::no, test::CanCompare::yes, test::ProxyRef::yes>;

constexpr bool run_tests() {
// All (except contiguous) proxy reference types, since the algorithm doesn't really care.
using test::Common, test::Sized;

// both input, non-common, and sized or unsized
instantiator::call<in_test_range<Elem1, Sized::no>, in_test_range<Elem2, Sized::no>, out_test_iterator<Elem3>>();
instantiator::call<in_test_range<Elem1, Sized::yes>, in_test_range<Elem2, Sized::yes>, out_test_iterator<Elem3>>();
return true;
}

int main() {
STATIC_ASSERT(run_tests());
run_tests();
}
#endif // TEST_EVERYTHING
4 changes: 4 additions & 0 deletions tests/std/tests/P0896R4_ranges_alg_transform_unary/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
Loading