diff --git a/libcxx/docs/Status/RangesAlgorithms.csv b/libcxx/docs/Status/RangesAlgorithms.csv --- a/libcxx/docs/Status/RangesAlgorithms.csv +++ b/libcxx/docs/Status/RangesAlgorithms.csv @@ -51,8 +51,8 @@ Write,remove_copy_if,Nikolas Klauser,n/a,Not started Write,replace,Nikolas Klauser,`D126283 `_,✅ Write,replace_if,Nikolas Klauser,`D126283 `_,✅ -Write,replace_copy,Nikolas Klauser,n/a,Not started -Write,replace_copy_if,Nikolas Klauser,n/a,Not started +Write,replace_copy,Nikolas Klauser,`D126283 `_,✅ +Write,replace_copy_if,Nikolas Klauser,`D126283 `_,✅ Write,swap_ranges,Nikolas Klauser,`D116303 `_,✅ Write,reverse_copy,Nikolas Klauser,`D127211 `_,✅ Write,rotate_copy,Nikolas Klauser,`D127211 `_,✅ diff --git a/libcxx/include/__algorithm/ranges_replace_copy.h b/libcxx/include/__algorithm/ranges_replace_copy.h --- a/libcxx/include/__algorithm/ranges_replace_copy.h +++ b/libcxx/include/__algorithm/ranges_replace_copy.h @@ -10,19 +10,16 @@ #define _LIBCPP___ALGORITHM_RANGES_REPLACE_COPY_H #include <__algorithm/in_out_result.h> -#include <__algorithm/make_projected.h> -#include <__algorithm/replace_copy.h> +#include <__algorithm/ranges_replace_copy_if.h> #include <__config> #include <__functional/identity.h> #include <__functional/invoke.h> #include <__functional/ranges_operations.h> #include <__iterator/concepts.h> -#include <__iterator/iterator_traits.h> #include <__iterator/projected.h> #include <__ranges/access.h> #include <__ranges/concepts.h> #include <__ranges/dangling.h> -#include <__utility/forward.h> #include <__utility/move.h> #if !defined(_LIBCPP_HAS_NO_PRAGMA_SYSTEM_HEADER) @@ -40,35 +37,45 @@ namespace __replace_copy { -struct __fn { - - template _Sent, class _Type1, class _Type2, - output_iterator _OutIter, class _Proj = identity> - requires indirectly_copyable<_InIter, _OutIter> && - indirect_binary_predicate, const _Type1*> - _LIBCPP_HIDE_FROM_ABI constexpr - replace_copy_result<_InIter, _OutIter> - operator()(_InIter __first, _Sent __last, _OutIter __result, const _Type1& __old_value, const _Type2& __new_value, + struct __fn { + template _Sent, + class _OldType, + class _NewType, + output_iterator _OutIter, + class _Proj = identity> + requires indirectly_copyable<_InIter, _OutIter> && + indirect_binary_predicate, const _OldType*> + _LIBCPP_HIDE_FROM_ABI constexpr replace_copy_result<_InIter, _OutIter> + operator()(_InIter __first, + _Sent __last, + _OutIter __result, + const _OldType& __old_value, + const _NewType& __new_value, _Proj __proj = {}) const { - // TODO: implement - (void)__first; (void)__last; (void)__result; (void)__old_value; (void)__new_value; (void)__proj; - return {}; - } - - template _OutIter, - class _Proj = identity> - requires indirectly_copyable, _OutIter> && - indirect_binary_predicate, _Proj>, const _Type1*> - _LIBCPP_HIDE_FROM_ABI constexpr - replace_copy_result, _OutIter> - operator()(_Range&& __range, _OutIter __result, const _Type1& __old_value, const _Type2& __new_value, + auto __pred = [&](const auto& __value) { return __value == __old_value; }; + return ranges::__replace_copy_if_impl( + std::move(__first), std::move(__last), std::move(__result), __pred, __new_value, __proj); + } + + template _OutIter, + class _Proj = identity> + requires indirectly_copyable, _OutIter> && + indirect_binary_predicate, _Proj>, const _OldType*> + _LIBCPP_HIDE_FROM_ABI constexpr replace_copy_result, _OutIter> + operator()(_Range&& __range, + _OutIter __result, + const _OldType& __old_value, + const _NewType& __new_value, _Proj __proj = {}) const { - // TODO: implement - (void)__range; (void)__result; (void)__old_value; (void)__new_value; (void)__proj; - return {}; - } - -}; + auto __pred = [&](const auto& __value) { return __value == __old_value; }; + return ranges::__replace_copy_if_impl( + ranges::begin(__range), ranges::end(__range), std::move(__result), __pred, __new_value, __proj); + } + }; } // namespace __replace_copy diff --git a/libcxx/include/__algorithm/ranges_replace_copy_if.h b/libcxx/include/__algorithm/ranges_replace_copy_if.h --- a/libcxx/include/__algorithm/ranges_replace_copy_if.h +++ b/libcxx/include/__algorithm/ranges_replace_copy_if.h @@ -10,19 +10,14 @@ #define _LIBCPP___ALGORITHM_RANGES_REPLACE_COPY_IF_H #include <__algorithm/in_out_result.h> -#include <__algorithm/make_projected.h> -#include <__algorithm/replace_copy_if.h> #include <__config> #include <__functional/identity.h> #include <__functional/invoke.h> -#include <__functional/ranges_operations.h> #include <__iterator/concepts.h> -#include <__iterator/iterator_traits.h> #include <__iterator/projected.h> #include <__ranges/access.h> #include <__ranges/concepts.h> #include <__ranges/dangling.h> -#include <__utility/forward.h> #include <__utility/move.h> #if !defined(_LIBCPP_HAS_NO_PRAGMA_SYSTEM_HEADER) @@ -38,34 +33,49 @@ template using replace_copy_if_result = in_out_result<_InIter, _OutIter>; -namespace __replace_copy_if { - -struct __fn { - - template _Sent, class _Type, output_iterator _OutIter, - class _Proj = identity, indirect_unary_predicate> _Pred> - requires indirectly_copyable<_InIter, _OutIter> - _LIBCPP_HIDE_FROM_ABI constexpr - replace_copy_if_result<_InIter, _OutIter> - operator()(_InIter __first, _Sent __last, _OutIter __result, _Pred __pred, const _Type& __new_value, - _Proj __proj = {}) const { - // TODO: implement - (void)__first; (void)__last; (void)__result; (void)__pred; (void)__new_value; (void)__proj; - return {}; +template +_LIBCPP_HIDE_FROM_ABI constexpr replace_copy_if_result<_InIter, _OutIter> __replace_copy_if_impl( + _InIter __first, _Sent __last, _OutIter __result, _Pred& __pred, const _Type& __new_value, _Proj& __proj) { + while (__first != __last) { + if (std::invoke(__pred, std::invoke(__proj, *__first))) + *__result = __new_value; + else + *__result = *__first; + ++__first; + ++__result; } + return {std::move(__first), std::move(__result)}; +} - template _OutIter, class _Proj = identity, - indirect_unary_predicate, _Proj>> _Pred> - requires indirectly_copyable, _OutIter> - _LIBCPP_HIDE_FROM_ABI constexpr - replace_copy_if_result, _OutIter> - operator()(_Range&& __range, _OutIter __result, _Pred __pred, const _Type& __new_value, _Proj __proj = {}) const { - // TODO: implement - (void)__range; (void)__result; (void)__pred; (void)__new_value; (void)__proj; - return {}; - } +namespace __replace_copy_if { -}; + struct __fn { + template _Sent, + class _Type, + output_iterator _OutIter, + class _Proj = identity, + indirect_unary_predicate> _Pred> + requires indirectly_copyable<_InIter, _OutIter> + _LIBCPP_HIDE_FROM_ABI constexpr replace_copy_if_result<_InIter, _OutIter> operator()( + _InIter __first, _Sent __last, _OutIter __result, _Pred __pred, const _Type& __new_value, _Proj __proj = {}) + const { + return ranges::__replace_copy_if_impl( + std::move(__first), std::move(__last), std::move(__result), __pred, __new_value, __proj); + } + + template _OutIter, + class _Proj = identity, + indirect_unary_predicate, _Proj>> _Pred> + requires indirectly_copyable, _OutIter> + _LIBCPP_HIDE_FROM_ABI constexpr replace_copy_if_result, _OutIter> + operator()(_Range&& __range, _OutIter __result, _Pred __pred, const _Type& __new_value, _Proj __proj = {}) const { + return ranges::__replace_copy_if_impl( + ranges::begin(__range), ranges::end(__range), std::move(__result), __pred, __new_value, __proj); + } + }; } // namespace __replace_copy_if diff --git a/libcxx/include/algorithm b/libcxx/include/algorithm --- a/libcxx/include/algorithm +++ b/libcxx/include/algorithm @@ -703,7 +703,7 @@ set_symmetric_difference(I1 first1, S1 last1, I2 first2, S2 last2, O result, Comp comp = {}, Proj1 proj1 = {}, Proj2 proj2 = {}); // since C++20 - + template requires mergeable, iterator_t, O, Comp, Proj1, Proj2> @@ -711,7 +711,7 @@ borrowed_iterator_t, O> set_symmetric_difference(R1&& r1, R2&& r2, O result, Comp comp = {}, Proj1 proj1 = {}, Proj2 proj2 = {}); // since C++20 - + template using set_union_result = in_in_out_result; // since C++20 @@ -729,6 +729,38 @@ constexpr set_union_result, borrowed_iterator_t, O> set_union(R1&& r1, R2&& r2, O result, Comp comp = {}, Proj1 proj1 = {}, Proj2 proj2 = {}); // since C++20 + + template S, class T1, class T2, + output_iterator O, class Proj = identity> + requires indirectly_copyable && + indirect_binary_predicate, const T1*> + constexpr replace_copy_result + replace_copy(I first, S last, O result, const T1& old_value, const T2& new_value, + Proj proj = {}); // Since C++20 + + template O, + class Proj = identity> + requires indirectly_copyable, O> && + indirect_binary_predicate, Proj>, const T1*> + constexpr replace_copy_result, O> + replace_copy(R&& r, O result, const T1& old_value, const T2& new_value, + Proj proj = {}); // Since C++20 + + template S, class T, output_iterator O, + class Proj = identity, indirect_unary_predicate> Pred> + requires indirectly_copyable + constexpr replace_copy_if_result + replace_copy_if(I first, S last, O result, Pred pred, const T& new_value, + Proj proj = {}); // Since C++20 + + template O, class Proj = identity, + indirect_unary_predicate, Proj>> Pred> + requires indirectly_copyable, O> + constexpr replace_copy_if_result, O> + replace_copy_if(R&& r, O result, Pred pred, const T& new_value, + Proj proj = {}); // Since C++20 + } constexpr bool // constexpr in C++20 @@ -1501,6 +1533,8 @@ #include <__algorithm/ranges_remove.h> #include <__algorithm/ranges_remove_if.h> #include <__algorithm/ranges_replace.h> +#include <__algorithm/ranges_replace_copy.h> +#include <__algorithm/ranges_replace_copy_if.h> #include <__algorithm/ranges_replace_if.h> #include <__algorithm/ranges_reverse.h> #include <__algorithm/ranges_reverse_copy.h> diff --git a/libcxx/test/libcxx/algorithms/ranges_robust_against_copying_comparators.pass.cpp b/libcxx/test/libcxx/algorithms/ranges_robust_against_copying_comparators.pass.cpp --- a/libcxx/test/libcxx/algorithms/ranges_robust_against_copying_comparators.pass.cpp +++ b/libcxx/test/libcxx/algorithms/ranges_robust_against_copying_comparators.pass.cpp @@ -191,8 +191,8 @@ //(void)std::ranges::remove_copy_if(a, first2, UnaryTrue(&copies)); assert(copies == 0); (void)std::ranges::remove_if(first, last, UnaryTrue(&copies)); assert(copies == 0); (void)std::ranges::remove_if(a, UnaryTrue(&copies)); assert(copies == 0); - //(void)std::ranges::replace_copy_if(first, last, first2, UnaryTrue(&copies), value); assert(copies == 0); - //(void)std::ranges::replace_copy_if(a, first2, UnaryTrue(&copies), value); assert(copies == 0); + (void)std::ranges::replace_copy_if(first, last, first2, UnaryTrue(&copies), value); assert(copies == 0); + (void)std::ranges::replace_copy_if(a, first2, UnaryTrue(&copies), value); assert(copies == 0); (void)std::ranges::replace_if(first, last, UnaryTrue(&copies), value); assert(copies == 0); (void)std::ranges::replace_if(a, UnaryTrue(&copies), value); assert(copies == 0); (void)std::ranges::search(first, last, first2, mid2, Equal(&copies)); assert(copies == 0); diff --git a/libcxx/test/libcxx/algorithms/ranges_robust_against_copying_projections.pass.cpp b/libcxx/test/libcxx/algorithms/ranges_robust_against_copying_projections.pass.cpp --- a/libcxx/test/libcxx/algorithms/ranges_robust_against_copying_projections.pass.cpp +++ b/libcxx/test/libcxx/algorithms/ranges_robust_against_copying_projections.pass.cpp @@ -178,10 +178,10 @@ (void)std::ranges::remove(a, value, Proj(&copies)); assert(copies == 0); (void)std::ranges::remove_if(first, last, UnaryTrue(), Proj(&copies)); assert(copies == 0); (void)std::ranges::remove_if(a, UnaryTrue(), Proj(&copies)); assert(copies == 0); - //(void)std::ranges::replace_copy(first, last, first2, value, T(), Proj(&copies)); assert(copies == 0); - //(void)std::ranges::replace_copy(a, first2, value, T(), Proj(&copies)); assert(copies == 0); - //(void)std::ranges::replace_copy_if(first, last, first2, UnaryTrue(), T(), Proj(&copies)); assert(copies == 0); - //(void)std::ranges::replace_copy_if(a, first2, UnaryTrue(), T(), Proj(&copies)); assert(copies == 0); + (void)std::ranges::replace_copy(first, last, first2, value, T(), Proj(&copies)); assert(copies == 0); + (void)std::ranges::replace_copy(a, first2, value, T(), Proj(&copies)); assert(copies == 0); + (void)std::ranges::replace_copy_if(first, last, first2, UnaryTrue(), T(), Proj(&copies)); assert(copies == 0); + (void)std::ranges::replace_copy_if(a, first2, UnaryTrue(), T(), Proj(&copies)); assert(copies == 0); (void)std::ranges::replace(first, last, value, T(), Proj(&copies)); assert(copies == 0); (void)std::ranges::replace(a, value, T(), Proj(&copies)); assert(copies == 0); (void)std::ranges::replace_if(first, last, UnaryTrue(), T(), Proj(&copies)); assert(copies == 0); diff --git a/libcxx/test/std/algorithms/alg.modifying.operations/alg.replace/ranges_replace_copy.pass.cpp b/libcxx/test/std/algorithms/alg.modifying.operations/alg.replace/ranges_replace_copy.pass.cpp --- a/libcxx/test/std/algorithms/alg.modifying.operations/alg.replace/ranges_replace_copy.pass.cpp +++ b/libcxx/test/std/algorithms/alg.modifying.operations/alg.replace/ranges_replace_copy.pass.cpp @@ -26,8 +26,6 @@ // replace_copy(R&& r, O result, const T1& old_value, const T2& new_value, // Proj proj = {}); // Since C++20 -// TODO: synopsis - #include #include #include @@ -37,12 +35,185 @@ #include "almost_satisfies_types.h" #include "test_iterators.h" -// TODO: SFINAE tests. +template , class OutIter = int*> +concept HasRemoveIfIt = + requires(Iter first, Sent last, OutIter result) { std::ranges::replace_copy(first, last, result, 0, 0); }; + +static_assert(HasRemoveIfIt); +static_assert(!HasRemoveIfIt); +static_assert(!HasRemoveIfIt); +static_assert(!HasRemoveIfIt); +static_assert(!HasRemoveIfIt); +static_assert(!HasRemoveIfIt); +static_assert(!HasRemoveIfIt); +static_assert(!HasRemoveIfIt); + +template +concept HasReplaceCopyR = requires(Range range, OutIter result) { std::ranges::replace_copy(range, result, 0, 0); }; + +static_assert(HasReplaceCopyR>); +static_assert(!HasReplaceCopyR); +static_assert(!HasReplaceCopyR); +static_assert(!HasReplaceCopyR); +static_assert(!HasReplaceCopyR); +static_assert(!HasReplaceCopyR); +static_assert(!HasReplaceCopyR, OutputIteratorNotIndirectlyWritable>); +static_assert(!HasReplaceCopyR, OutputIteratorNotInputOrOutputIterator>); + +template +struct Data { + std::array input; + int old_value; + int new_value; + std::array expected; +}; + +template +constexpr void test(Data d) { + { // iterator overload + std::array output; + + auto first = InIter(d.input.data()); + auto last = Sent(InIter(d.input.data() + d.input.size())); + auto result = OutIter(output.data()); + + std::same_as> decltype(auto) ret = + std::ranges::replace_copy(std::move(first), std::move(last), std::move(result), d.old_value, d.new_value); + assert(base(ret.in) == d.input.data() + d.input.size()); + assert(base(ret.out) == output.data() + output.size()); + assert(d.expected == output); + } + { // range overload + std::array output; + + auto range = std::ranges::subrange(InIter(d.input.data()), Sent(InIter(d.input.data() + d.input.size()))); + auto result = OutIter(output.data()); + + std::same_as> decltype(auto) ret = + std::ranges::replace_copy(range, result, d.old_value, d.new_value); + assert(base(ret.in) == d.input.data() + d.input.size()); + assert(base(ret.out) == output.data() + output.size()); + assert(d.expected == output); + } +} + +template +constexpr void tests() { + // simple test + test({.input = {1, 2, 3, 4}, .old_value = 2, .new_value = 5, .expected = {1, 5, 3, 4}}); + // empty range + test({.input = {}, .old_value = 2, .new_value = 5, .expected = {}}); + // all elements match + test({.input = {1, 1, 1, 1}, .old_value = 1, .new_value = 2, .expected = {2, 2, 2, 2}}); + // no element matches + test({.input = {1, 1, 1, 1}, .old_value = 2, .new_value = 3, .expected = {1, 1, 1, 1}}); + // old_value and new_value are identical - match + test({.input = {1, 1, 1, 1}, .old_value = 1, .new_value = 1, .expected = {1, 1, 1, 1}}); + // old_value and new_value are identical - no match + test({.input = {1, 1, 1, 1}, .old_value = 2, .new_value = 2, .expected = {1, 1, 1, 1}}); + // more elements + test( + {.input = {1, 2, 3, 4, 5, 6, 7}, .old_value = 2, .new_value = 3, .expected = {1, 3, 3, 4, 5, 6, 7}}); + // single element - match + test({.input = {1}, .old_value = 1, .new_value = 5, .expected = {5}}); + // single element - no match + test({.input = {2}, .old_value = 1, .new_value = 5, .expected = {2}}); +} + +template +constexpr void test_output_iterators() { + tests>(); + tests>(); + tests>(); + tests>(); + tests>(); + tests(); +} + +template +constexpr void test_sentinels() { + test_output_iterators(); + test_output_iterators>(); + test_output_iterators>(); +} constexpr bool test() { - // TODO: main tests. - // TODO: A custom comparator works. - // TODO: A custom projection works. + test_output_iterators, sentinel_wrapper>>(); + test_output_iterators, sentinel_wrapper>>(); + test_sentinels>(); + test_sentinels>(); + test_sentinels>(); + test_sentinels>(); + test_sentinels(); + test_sentinels(); + + { // check that a custom projection works + struct S { + int i; + }; + { // iterator overload + S a[] = {{1}, {2}, {3}, {4}}; + S b[4]; + auto ret = std::ranges::replace_copy(std::begin(a), std::end(a), std::begin(b), 1, S{2}, &S::i); + assert(ret.in == std::end(a)); + assert(ret.out == std::end(b)); + } + { // range overload + S a[] = {{1}, {2}, {3}, {4}}; + S b[4]; + auto ret = std::ranges::replace_copy(a, std::begin(b), 1, S{2}, &S::i); + assert(ret.in == std::end(a)); + assert(ret.out == std::end(b)); + } + } + + { // check complexity requirements + { // iterator overload + int proj_count = 0; + auto proj = [&](int i) { + ++proj_count; + return i; + }; + int a[] = {1, 2, 3, 4}; + int b[4]; + std::ranges::replace_copy(std::begin(a), std::end(a), std::begin(b), 0, 0, proj); + assert(proj_count == 4); + } + { // range overload + int proj_count = 0; + auto proj = [&](int i) { + ++proj_count; + return i; + }; + int a[] = {1, 2, 3, 4}; + int b[4]; + std::ranges::replace_copy(a, std::begin(b), 0, 0, proj); + assert(proj_count == 4); + } + } + + { // using different types the old and new value works + struct S { + constexpr operator int() const { return 0; } + constexpr bool operator==(const S&) const = default; + constexpr bool operator==(int i) const { return i == 0; } + }; + struct T { + constexpr operator int() const { return 1; } + }; + { + int a[] = {0, 1, 2, 3}; + int b[4]; + std::ranges::replace_copy(std::begin(a), std::end(a), std::begin(b), S{}, T{}); + assert(std::ranges::equal(b, std::array{1, 1, 2, 3})); + } + { + int a[] = {0, 1, 2, 3}; + int b[4]; + std::ranges::replace_copy(a, std::begin(b), S{}, T{}); + assert(std::ranges::equal(b, std::array{1, 1, 2, 3})); + } + } return true; } diff --git a/libcxx/test/std/algorithms/alg.modifying.operations/alg.replace/ranges_replace_copy_if.pass.cpp b/libcxx/test/std/algorithms/alg.modifying.operations/alg.replace/ranges_replace_copy_if.pass.cpp --- a/libcxx/test/std/algorithms/alg.modifying.operations/alg.replace/ranges_replace_copy_if.pass.cpp +++ b/libcxx/test/std/algorithms/alg.modifying.operations/alg.replace/ranges_replace_copy_if.pass.cpp @@ -34,12 +34,187 @@ #include "almost_satisfies_types.h" #include "test_iterators.h" -// TODO: SFINAE tests. +struct FalsePredicate { + constexpr bool operator()(int) { return false; } +}; + +template , class OutIter = int*> +concept HasRemoveIfIt = requires( + Iter first, Sent last, OutIter result) { std::ranges::replace_copy_if(first, last, result, FalsePredicate{}, 0); }; + +static_assert(HasRemoveIfIt); +static_assert(!HasRemoveIfIt); +static_assert(!HasRemoveIfIt); +static_assert(!HasRemoveIfIt); +static_assert(!HasRemoveIfIt); +static_assert(!HasRemoveIfIt); +static_assert(!HasRemoveIfIt); +static_assert(!HasRemoveIfIt); + +template +concept HasReplaceCopyR = + requires(Range range, OutIter result) { std::ranges::replace_copy_if(range, result, FalsePredicate{}, 0); }; + +static_assert(HasReplaceCopyR>); +static_assert(!HasReplaceCopyR); +static_assert(!HasReplaceCopyR); +static_assert(!HasReplaceCopyR); +static_assert(!HasReplaceCopyR); +static_assert(!HasReplaceCopyR); +static_assert(!HasReplaceCopyR, OutputIteratorNotIndirectlyWritable>); +static_assert(!HasReplaceCopyR, OutputIteratorNotInputOrOutputIterator>); + +template +struct Data { + std::array input; + int cutoff; + int new_value; + std::array expected; +}; + +template +constexpr void test(Data d) { + { // iterator overload + std::array output; + + auto first = InIter(d.input.data()); + auto last = Sent(InIter(d.input.data() + d.input.size())); + auto result = OutIter(output.data()); + + auto pred = [&](int i) { return i < d.cutoff; }; + + std::same_as> decltype(auto) ret = + std::ranges::replace_copy_if(std::move(first), std::move(last), std::move(result), pred, d.new_value); + assert(base(ret.in) == d.input.data() + d.input.size()); + assert(base(ret.out) == output.data() + output.size()); + assert(d.expected == output); + } + { // range overload + std::array output; + + auto range = std::ranges::subrange(InIter(d.input.data()), Sent(InIter(d.input.data() + d.input.size()))); + auto result = OutIter(output.data()); + + auto pred = [&](int i) { return i < d.cutoff; }; + + std::same_as> decltype(auto) ret = + std::ranges::replace_copy_if(range, result, pred, d.new_value); + assert(base(ret.in) == d.input.data() + d.input.size()); + assert(base(ret.out) == output.data() + output.size()); + assert(d.expected == output); + } +} + +template +constexpr void tests() { + // simple test + test({.input = {1, 2, 3, 4}, .cutoff = 2, .new_value = 5, .expected = {5, 2, 3, 4}}); + // empty range + test({.input = {}, .cutoff = 2, .new_value = 5, .expected = {}}); + // all elements match + test({.input = {1, 1, 1, 1}, .cutoff = 2, .new_value = 2, .expected = {2, 2, 2, 2}}); + // no element matches + test({.input = {1, 1, 1, 1}, .cutoff = 1, .new_value = 3, .expected = {1, 1, 1, 1}}); + // more elements + test( + {.input = {1, 2, 3, 4, 5, 6, 7}, .cutoff = 3, .new_value = 3, .expected = {3, 3, 3, 4, 5, 6, 7}}); + // single element - match + test({.input = {1}, .cutoff = 2, .new_value = 5, .expected = {5}}); + // single element - no match + test({.input = {2}, .cutoff = 2, .new_value = 5, .expected = {2}}); +} + +template +constexpr void test_output_iterators() { + tests>(); + tests>(); + tests>(); + tests>(); + tests>(); + tests(); +} + +template +constexpr void test_sentinels() { + test_output_iterators(); + test_output_iterators>(); + test_output_iterators>(); +} constexpr bool test() { - // TODO: main tests. - // TODO: A custom comparator works. - // TODO: A custom projection works. + test_output_iterators, sentinel_wrapper>>(); + test_output_iterators, sentinel_wrapper>>(); + test_sentinels>(); + test_sentinels>(); + test_sentinels>(); + test_sentinels>(); + test_sentinels(); + test_sentinels(); + + { // check that a custom projection works + struct S { + int i; + }; + { // iterator overload + S a[] = {{1}, {2}, {3}, {4}}; + S b[4]; + auto ret = std::ranges::replace_copy_if(std::begin(a), std::end(a), std::begin(b), FalsePredicate{}, S{2}, &S::i); + assert(ret.in == std::end(a)); + assert(ret.out == std::end(b)); + assert(std::ranges::equal(a, b, {}, &S::i, &S::i)); + } + { // range overload + S a[] = {{1}, {2}, {3}, {4}}; + S b[4]; + auto ret = std::ranges::replace_copy_if(a, std::begin(b), FalsePredicate{}, S{2}, &S::i); + assert(ret.in == std::end(a)); + assert(ret.out == std::end(b)); + assert(std::ranges::equal(a, b, {}, &S::i, &S::i)); + } + } + + { // check complexity requirements + { // iterator overload + int proj_count = 0; + auto proj = [&](int i) { + ++proj_count; + return i; + }; + int a[] = {1, 2, 3, 4}; + int b[4]; + std::ranges::replace_copy_if(std::begin(a), std::end(a), std::begin(b), FalsePredicate{}, 0, proj); + assert(proj_count == 4); + } + { // range overload + int proj_count = 0; + auto proj = [&](int i) { + ++proj_count; + return i; + }; + int a[] = {1, 2, 3, 4}; + int b[4]; + std::ranges::replace_copy_if(a, std::begin(b), FalsePredicate{}, 0, proj); + assert(proj_count == 4); + } + } + + { // using different types the old and new value works + struct S { + constexpr operator int() const { return 1; } + }; + { + int a[] = {0, 0, 2, 3}; + int b[4]; + std::ranges::replace_copy_if(std::begin(a), std::end(a), std::begin(b), [](int i) { return i < 2; }, S{}); + assert(std::ranges::equal(b, std::array{1, 1, 2, 3})); + } + { + int a[] = {0, 0, 2, 3}; + int b[4]; + std::ranges::replace_copy_if(a, std::begin(b), [](int i) { return i < 2; }, S{}); + assert(std::ranges::equal(b, std::array{1, 1, 2, 3})); + } + } return true; } diff --git a/libcxx/test/std/algorithms/ranges_robust_against_nonbool_predicates.compile.pass.cpp b/libcxx/test/std/algorithms/ranges_robust_against_nonbool_predicates.compile.pass.cpp --- a/libcxx/test/std/algorithms/ranges_robust_against_nonbool_predicates.compile.pass.cpp +++ b/libcxx/test/std/algorithms/ranges_robust_against_nonbool_predicates.compile.pass.cpp @@ -141,8 +141,8 @@ //std::ranges::replace_if(in.begin(), in.end(), unary_pred, x); //std::ranges::replace_if(in, unary_pred, x); // replace_copy_if - //std::ranges::replace_copy_if(in.begin(), in.end(), out, unary_pred, x); - //std::ranges::replace_copy_if(in, out, unary_pred, x); + std::ranges::replace_copy_if(in.begin(), in.end(), out, unary_pred, x); + std::ranges::replace_copy_if(in, out, unary_pred, x); //in_out_pred(std::ranges::unique_copy, in, out, binary_pred); // partition_copy diff --git a/libcxx/test/std/algorithms/ranges_robust_against_omitting_invoke.compile.pass.cpp b/libcxx/test/std/algorithms/ranges_robust_against_omitting_invoke.compile.pass.cpp --- a/libcxx/test/std/algorithms/ranges_robust_against_omitting_invoke.compile.pass.cpp +++ b/libcxx/test/std/algorithms/ranges_robust_against_omitting_invoke.compile.pass.cpp @@ -176,11 +176,11 @@ std::ranges::replace_if(in.begin(), in.end(), &Foo::unary_pred, a, &Bar::val); std::ranges::replace_if(in, &Foo::unary_pred, a, &Bar::val); // replace_copy - //std::ranges::replace_copy(in.begin(), in.end(), out, x, a, &Bar::val); - //std::ranges::replace_copy(in, out, x, a, &Bar::val); + std::ranges::replace_copy(in.begin(), in.end(), out, x, a, &Bar::val); + std::ranges::replace_copy(in, out, x, a, &Bar::val); // replace_copy_if - //std::ranges::replace_copy_if(in.begin(), in.end(), out, pred, a, &Bar::val); - //std::ranges::replace_copy_if(in, out, pred, a, &Bar::val); + std::ranges::replace_copy_if(in.begin(), in.end(), out, &Foo::unary_pred, a, &Bar::val); + std::ranges::replace_copy_if(in, out, &Foo::unary_pred, a, &Bar::val); // `swap_ranges` has neither a projection nor a predicate. // `reverse_copy` has neither a projection nor a predicate. // `rotate_copy` has neither a projection nor a predicate.