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
185 changes: 185 additions & 0 deletions stl/inc/algorithm
Original file line number Diff line number Diff line change
Expand Up @@ -3286,6 +3286,64 @@ void replace(_ExPo&& _Exec, const _FwdIt _First, const _FwdIt _Last, const _Ty&
const _Ty& _Newval) noexcept; // terminates
#endif // _HAS_CXX17

#ifdef __cpp_lib_concepts
namespace ranges {
// VARIABLE ranges::replace
class _Replace_fn : private _Not_quite_object {
public:
using _Not_quite_object::_Not_quite_object;

// clang-format off
template <input_iterator _It, sentinel_for<_It> _Se, class _Ty1, class _Ty2, class _Pj = identity>
requires indirectly_writable<_It, const _Ty2&>
&& indirect_binary_predicate<ranges::equal_to, projected<_It, _Pj>, const _Ty1*>
constexpr _It operator()(
_It _First, _Se _Last, const _Ty1& _Oldval, const _Ty2& _Newval, _Pj _Proj = {}) const {
_Adl_verify_range(_First, _Last);
auto _UResult = _Replace_unchecked(
_Get_unwrapped(_STD move(_First)), _Get_unwrapped(_STD move(_Last)), _Oldval, _Newval, _Pass_fn(_Proj));

_Seek_wrapped(_First, _STD move(_UResult));
return _First;
}

template <input_range _Rng, class _Ty1, class _Ty2, class _Pj = identity>
requires indirectly_writable<iterator_t<_Rng>, const _Ty2&>
&& indirect_binary_predicate<ranges::equal_to, projected<iterator_t<_Rng>, _Pj>, const _Ty1*>
constexpr borrowed_iterator_t<_Rng> operator()(
_Rng&& _Range, const _Ty1& _Oldval, const _Ty2& _Newval, _Pj _Proj = {}) const {
auto _First = _RANGES begin(_Range);
auto _UResult = _Replace_unchecked(
_Get_unwrapped(_STD move(_First)), _Uend(_Range), _Oldval, _Newval, _Pass_fn(_Proj));

_Seek_wrapped(_First, _STD move(_UResult));
return _First;
}
// clang-format on
private:
template <class _It, class _Se, class _Ty1, class _Ty2, class _Pj>
_NODISCARD static constexpr _It _Replace_unchecked(
_It _First, const _Se _Last, const _Ty1& _Oldval, const _Ty2& _Newval, _Pj _Proj) {
// replace projected _Oldval with _Newval in [_First, _Last)
_STL_INTERNAL_STATIC_ASSERT(input_iterator<_It>);
_STL_INTERNAL_STATIC_ASSERT(sentinel_for<_Se, _It>);
_STL_INTERNAL_STATIC_ASSERT(indirectly_writable<_It, const _Ty2&>);
_STL_INTERNAL_STATIC_ASSERT(indirect_binary_predicate<ranges::equal_to, projected<_It, _Pj>, const _Ty1*>);

for (; _First != _Last; ++_First) {
if (_STD invoke(_Proj, *_First) == _Oldval) {
*_First = _Newval;
}
}

return _First;
}
};

inline constexpr _Replace_fn replace{_Not_quite_object::_Construct_tag{}};
} // namespace ranges
#endif // __cpp_lib_concepts

// FUNCTION TEMPLATE replace_if
template <class _FwdIt, class _Pr, class _Ty>
_CONSTEXPR20 void replace_if(const _FwdIt _First, const _FwdIt _Last, _Pr _Pred, const _Ty& _Val) {
Expand Down Expand Up @@ -3394,6 +3452,72 @@ _FwdIt2 replace_copy(_ExPo&&, _FwdIt1 _First, _FwdIt1 _Last, _FwdIt2 _Dest, cons
}
#endif // _HAS_CXX17

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

// VARIABLE ranges::replace_copy
class _Replace_copy_fn : private _Not_quite_object {
public:
using _Not_quite_object::_Not_quite_object;

// clang-format off
template <input_iterator _It, sentinel_for<_It> _Se, class _Ty1, class _Ty2, output_iterator<const _Ty2&> _Out,
class _Pj = identity>
requires indirectly_copyable<_It, _Out>
&& indirect_binary_predicate<ranges::equal_to, projected<_It, _Pj>, const _Ty1*>
constexpr replace_copy_result<_It, _Out> operator()(
_It _First, _Se _Last, _Out _Result, const _Ty1& _Oldval, const _Ty2& _Newval, _Pj _Proj = {}) const {
_Adl_verify_range(_First, _Last);
auto _UResult = _Replace_copy_unchecked(_Get_unwrapped(_STD move(_First)), _Get_unwrapped(_STD move(_Last)),
_STD move(_Result), _Oldval, _Newval, _Pass_fn(_Proj));

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

template <input_range _Rng, class _Ty1, class _Ty2, output_iterator<const _Ty2&> _Out, class _Pj = identity>
requires indirectly_copyable<iterator_t<_Rng>, _Out>
&& indirect_binary_predicate<ranges::equal_to, projected<iterator_t<_Rng>, _Pj>, const _Ty1*>
constexpr replace_copy_result<borrowed_iterator_t<_Rng>, _Out> operator()(
_Rng&& _Range, _Out _Result, const _Ty1& _Oldval, const _Ty2& _Newval, _Pj _Proj = {}) const {
auto _First = _RANGES begin(_Range);
auto _UResult = _Replace_copy_unchecked(_Get_unwrapped(_STD move(_First)), _Uend(_Range),
_STD move(_Result), _Oldval, _Newval, _Pass_fn(_Proj));

_Seek_wrapped(_First, _STD move(_UResult.in));
return {_STD move(_First), _STD move(_UResult.out)};
}
// clang-format on
private:
template <class _It, class _Se, class _Ty1, class _Ty2, class _Out, class _Pj>
_NODISCARD static constexpr replace_copy_result<_It, _Out> _Replace_copy_unchecked(
_It _First, const _Se _Last, _Out _Result, const _Ty1& _Oldval, const _Ty2& _Newval, _Pj _Proj) {
// copy [_First, _Last) to _Result while replacing projected _Oldval with _Newval
_STL_INTERNAL_STATIC_ASSERT(input_iterator<_It>);
_STL_INTERNAL_STATIC_ASSERT(sentinel_for<_Se, _It>);
_STL_INTERNAL_STATIC_ASSERT(output_iterator<_Out, const _Ty2&>);
_STL_INTERNAL_STATIC_ASSERT(indirectly_copyable<_It, _Out>);
_STL_INTERNAL_STATIC_ASSERT(indirect_binary_predicate<equal_to, projected<_It, _Pj>, const _Ty1*>);

for (; _First != _Last; ++_First, (void) ++_Result) {
if (_STD invoke(_Proj, *_First) == _Oldval) {
*_Result = _Newval;
} else {
*_Result = *_First;
}
}

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

inline constexpr _Replace_copy_fn replace_copy{_Not_quite_object::_Construct_tag{}};
} // namespace ranges
#endif // __cpp_lib_concepts

// FUNCTION TEMPLATE replace_copy_if
template <class _InIt, class _OutIt, class _Pr, class _Ty>
_CONSTEXPR20 _OutIt replace_copy_if(_InIt _First, _InIt _Last, _OutIt _Dest, _Pr _Pred, const _Ty& _Val) {
Expand Down Expand Up @@ -3428,6 +3552,67 @@ _FwdIt2 replace_copy_if(_ExPo&&, _FwdIt1 _First, _FwdIt1 _Last, _FwdIt2 _Dest, _

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

// VARIABLE ranges::replace_copy_if
class _Replace_copy_if_fn : private _Not_quite_object {
public:
using _Not_quite_object::_Not_quite_object;

// clang-format off
template <input_iterator _It, sentinel_for<_It> _Se, class _Ty, output_iterator<const _Ty&> _Out,
class _Pj = identity, indirect_unary_predicate<projected<_It, _Pj>> _Pr>
requires indirectly_copyable<_It, _Out>
constexpr replace_copy_if_result<_It, _Out> operator()(
_It _First, _Se _Last, _Out _Result, _Pr _Pred, const _Ty& _Newval, _Pj _Proj = {}) const {
_Adl_verify_range(_First, _Last);
auto _UResult = _Replace_copy_if_unchecked(_Get_unwrapped(_STD move(_First)),
_Get_unwrapped(_STD move(_Last)), _STD move(_Result), _Pass_fn(_Pred), _Newval, _Pass_fn(_Proj));

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

template <input_range _Rng, class _Ty, output_iterator<const _Ty&> _Out, class _Pj = identity,
indirect_unary_predicate<projected<iterator_t<_Rng>, _Pj>> _Pr>
requires indirectly_copyable<iterator_t<_Rng>, _Out>
constexpr replace_copy_if_result<borrowed_iterator_t<_Rng>, _Out> operator()(
_Rng&& _Range, _Out _Result, _Pr _Pred, const _Ty& _Newval, _Pj _Proj = {}) const {
auto _First = _RANGES begin(_Range);
auto _UResult = _Replace_copy_if_unchecked(_Get_unwrapped(_STD move(_First)), _Uend(_Range),
_STD move(_Result), _Pass_fn(_Pred), _Newval, _Pass_fn(_Proj));

_Seek_wrapped(_First, _STD move(_UResult.in));
return {_STD move(_First), _STD move(_UResult.out)};
}
// clang-format on
private:
template <class _It, class _Se, class _Ty, class _Out, class _Pj, class _Pr>
_NODISCARD static constexpr replace_copy_if_result<_It, _Out> _Replace_copy_if_unchecked(
_It _First, const _Se _Last, _Out _Result, _Pr _Pred, const _Ty& _Newval, _Pj _Proj) {
// copy [_First, _Last) to _Result while replacing _Oldval with _Newval if projected _Oldval fulfills _Pred
_STL_INTERNAL_STATIC_ASSERT(input_iterator<_It>);
_STL_INTERNAL_STATIC_ASSERT(sentinel_for<_Se, _It>);
_STL_INTERNAL_STATIC_ASSERT(output_iterator<_Out, const _Ty&>);
_STL_INTERNAL_STATIC_ASSERT(indirectly_copyable<_It, _Out>);
_STL_INTERNAL_STATIC_ASSERT(indirect_unary_predicate<_Pr, projected<_It, _Pj>>);

for (; _First != _Last; ++_First, (void) ++_Result) {
if (_STD invoke(_Pred, _STD invoke(_Proj, *_First))) {
*_Result = _Newval;
} else {
*_Result = *_First;
}
}

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

inline constexpr _Replace_copy_if_fn replace_copy_if{_Not_quite_object::_Construct_tag{}};

// VARIABLE ranges::fill
class _Fill_fn : private _Not_quite_object {
public:
Expand Down
28 changes: 23 additions & 5 deletions tests/std/include/range_algorithm_support.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -627,7 +627,7 @@ template <class I1, class I2>
using BinaryPredicateFor = boolish (*)(std::iter_common_reference_t<I1>, std::iter_common_reference_t<I2>);

template <class Continuation, class Element>
struct with_writable_iterators {
struct with_output_iterators {
template <class... Args>
static constexpr void call() {
using namespace test;
Expand All @@ -638,10 +638,6 @@ struct with_writable_iterators {
iterator<output, Element, CanDifference::no, CanCompare::no, ProxyRef::no>>();
Continuation::template call<Args...,
iterator<output, Element, CanDifference::no, CanCompare::no, ProxyRef::yes>>();
Continuation::template call<Args...,
iterator<input, Element, CanDifference::no, CanCompare::no, ProxyRef::no>>();
Continuation::template call<Args...,
iterator<input, Element, CanDifference::no, CanCompare::no, ProxyRef::yes>>();
// For forward and bidi, Eq is necessarily true but Diff and Proxy may vary.
Continuation::template call<Args...,
iterator<fwd, Element, CanDifference::no, CanCompare::yes, ProxyRef::no>>();
Expand Down Expand Up @@ -670,6 +666,23 @@ struct with_writable_iterators {
}
};

template <class Continuation, class Element>
struct with_writable_iterators {
template <class... Args>
static constexpr void call() {
using namespace test;
using test::iterator;

// Diff and Eq are not significant for "lone" single-pass iterators, so we can ignore them here.
Continuation::template call<Args...,
iterator<input, Element, CanDifference::no, CanCompare::no, ProxyRef::no>>();
Continuation::template call<Args...,
iterator<input, Element, CanDifference::no, CanCompare::no, ProxyRef::yes>>();

with_output_iterators<Continuation, Element>::template call<Args...>();
}
};

template <class Continuation, class Element>
struct with_contiguous_ranges {
template <class... Args>
Expand Down Expand Up @@ -970,6 +983,11 @@ constexpr void test_contiguous() {
with_contiguous_ranges<Instantiator, Element>::call();
}

template <class Instantiator, class Element1, class Element2>
constexpr void input_range_output_iterator_permutations() {
with_input_ranges<with_output_iterators<Instantiator, Element2>, Element1>::call();
}

template <class Instantiator, class Element1, class Element2>
constexpr void test_in_in() {
with_input_ranges<with_input_ranges<Instantiator, Element2>, Element1>::call();
Expand Down
3 changes: 3 additions & 0 deletions tests/std/test.lst
Original file line number Diff line number Diff line change
Expand Up @@ -266,6 +266,9 @@ tests\P0896R4_ranges_alg_none_of
tests\P0896R4_ranges_alg_partition
tests\P0896R4_ranges_alg_partition_copy
tests\P0896R4_ranges_alg_partition_point
tests\P0896R4_ranges_alg_replace
tests\P0896R4_ranges_alg_replace_copy
tests\P0896R4_ranges_alg_replace_copy_if
tests\P0896R4_ranges_alg_replace_if
tests\P0896R4_ranges_alg_search
tests\P0896R4_ranges_alg_search_n
Expand Down
4 changes: 4 additions & 0 deletions tests/std/tests/P0896R4_ranges_alg_replace/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
48 changes: 48 additions & 0 deletions tests/std/tests/P0896R4_ranges_alg_replace/test.cpp
Original file line number Diff line number Diff line change
@@ -0,0 +1,48 @@
// 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 dangling story
STATIC_ASSERT(same_as<decltype(ranges::replace(borrowed<false>{}, 42, 5)), ranges::dangling>);
STATIC_ASSERT(same_as<decltype(ranges::replace(borrowed<true>{}, 42, 5)), int*>);

struct instantiator {
static constexpr P output[5] = {{0, 99}, {47, 1}, {2, 99}, {47, 1}, {4, 99}};

template <ranges::input_range Read>
static constexpr void call() {
using ranges::replace, ranges::iterator_t;
{ // Validate iterator + sentinel overload
P input[5] = {{0, 99}, {1, 47}, {2, 99}, {3, 47}, {4, 99}};
Read wrapped_input{input};

auto result = replace(wrapped_input.begin(), wrapped_input.end(), 47, P{47, 1}, get_second);
STATIC_ASSERT(same_as<decltype(result), iterator_t<Read>>);
assert(result == wrapped_input.end());
assert(ranges::equal(output, input));
}
{ // Validate range overload
P input[5] = {{0, 99}, {1, 47}, {2, 99}, {3, 47}, {4, 99}};
Read wrapped_input{input};

auto result = replace(wrapped_input, 47, P{47, 1}, get_second);
STATIC_ASSERT(same_as<decltype(result), iterator_t<Read>>);
assert(result == wrapped_input.end());
assert(ranges::equal(output, input));
}
}
};

int main() {
STATIC_ASSERT((test_in<instantiator, P>(), true));
test_in<instantiator, P>();
}
4 changes: 4 additions & 0 deletions tests/std/tests/P0896R4_ranges_alg_replace_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
62 changes: 62 additions & 0 deletions tests/std/tests/P0896R4_ranges_alg_replace_copy/test.cpp
Original file line number Diff line number Diff line change
@@ -0,0 +1,62 @@
// 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 replace_copy_result aliases in_out_result
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)),
ranges::replace_copy_result<ranges::dangling, int*>>);
STATIC_ASSERT(same_as<decltype(ranges::replace_copy(borrowed<true>{}, static_cast<int*>(nullptr), 42, 5)),
ranges::replace_copy_result<int*, int*>>);

struct instantiator {
static constexpr P input[5] = {{0, 99}, {1, 47}, {2, 99}, {3, 47}, {4, 99}};
static constexpr P expected[5] = {{0, 99}, {47, 1}, {2, 99}, {47, 1}, {4, 99}};

static constexpr void eq(P const (&output)[5]) {
// Extracted into a separate function to keep /analyze from exhausting the compiler heap
assert(ranges::equal(output, expected));
}

template <ranges::input_range Read, indirectly_writable<ranges::range_reference_t<Read>> Write>
static constexpr void call() {
using ranges::replace_copy, ranges::replace_copy_result, ranges::iterator_t;
{ // Validate iterator + sentinel overload
P output[5] = {{-1, -1}, {-1, -1}, {-1, -1}, {-1, -1}, {-1, -1}};
Read wrapped_input{input};

auto result =
replace_copy(wrapped_input.begin(), wrapped_input.end(), Write{output}, 47, P{47, 1}, get_second);
STATIC_ASSERT(same_as<decltype(result), replace_copy_result<iterator_t<Read>, Write>>);
assert(result.in == wrapped_input.end());
assert(result.out.peek() == output + 5);
eq(output);
}
{ // Validate range overload
P output[5] = {{-1, -1}, {-1, -1}, {-1, -1}, {-1, -1}, {-1, -1}};
Read wrapped_input{input};

auto result = replace_copy(wrapped_input, Write{output}, 47, P{47, 1}, get_second);
STATIC_ASSERT(same_as<decltype(result), replace_copy_result<iterator_t<Read>, Write>>);
assert(result.in == wrapped_input.end());
assert(result.out.peek() == output + 5);
eq(output);
}
}
};

int main() {
STATIC_ASSERT((input_range_output_iterator_permutations<instantiator, P const, P>(), true));
input_range_output_iterator_permutations<instantiator, P const, P>();
}
4 changes: 4 additions & 0 deletions tests/std/tests/P0896R4_ranges_alg_replace_copy_if/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