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
35 changes: 27 additions & 8 deletions stl/inc/algorithm
Original file line number Diff line number Diff line change
Expand Up @@ -336,21 +336,40 @@ namespace ranges {
indirectly_unary_invocable<projected<_It, _Pj>> _Fn>
constexpr for_each_result<_It, _Fn> operator()(_It _First, _Se _Last, _Fn _Func, _Pj _Proj = {}) const {
_Adl_verify_range(_First, _Last);
auto _UFirst = _Get_unwrapped(_STD move(_First));
const auto _ULast = _Get_unwrapped(_STD move(_Last));
for (; _UFirst != _ULast; ++_UFirst) {
_STD invoke(_Func, _STD invoke(_Proj, *_UFirst));
}

_Seek_wrapped(_First, _STD move(_UFirst));
return {_STD move(_First), _STD move(_Func)};
auto _UResult = _For_each_unchecked(
_Get_unwrapped(_STD move(_First)), _Get_unwrapped(_STD move(_Last)), _STD move(_Func), _Pass_fn(_Proj));

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

template <input_range _Rng, class _Pj = identity,
indirectly_unary_invocable<projected<iterator_t<_Rng>, _Pj>> _Fn>
constexpr for_each_result<borrowed_iterator_t<_Rng>, _Fn> operator()(
_Rng&& _Range, _Fn _Func, _Pj _Proj = {}) const {
return (*this)(_RANGES begin(_Range), _RANGES end(_Range), _STD move(_Func), _Pass_fn(_Proj));
auto _First = _RANGES begin(_Range);

auto _UResult = _For_each_unchecked(
_Get_unwrapped(_STD move(_First)), _Uend(_Range), _STD move(_Func), _Pass_fn(_Proj));

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

private:
template <class _It, class _Se, class _Pj, class _Fn>
_NODISCARD static constexpr for_each_result<_It, _Fn> _For_each_unchecked(
_It _First, const _Se _Last, _Fn _Func, _Pj _Proj) {
_STL_INTERNAL_STATIC_ASSERT(input_iterator<_It>);
_STL_INTERNAL_STATIC_ASSERT(sentinel_for<_Se, _It>);
_STL_INTERNAL_STATIC_ASSERT(indirectly_unary_invocable<_Fn, projected<_It, _Pj>>);

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

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

Expand Down
98 changes: 45 additions & 53 deletions tests/std/tests/P0896R4_ranges_alg_for_each/test.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -2,74 +2,66 @@
// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception

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

#include <range_algorithm_support.hpp>
using namespace std;
using P = pair<int, int>;

constexpr void smoke_test() {
using ranges::for_each, ranges::for_each_result, ranges::in_fun_result, ranges::iterator_t;
using std::identity, std::same_as;
using P = std::pair<int, int>;
using R = std::array<P, 3>;
constexpr auto incr = [](auto& y) { ++y; };

// Validate that for_each_result aliases in_fun_result
STATIC_ASSERT(same_as<for_each_result<int, double>, in_fun_result<int, double>>);
// Validate that for_each_result aliases in_fun_result
STATIC_ASSERT(same_as<ranges::for_each_result<int, double>, ranges::in_fun_result<int, double>>);

// Validate dangling story
STATIC_ASSERT(
same_as<decltype(for_each(borrowed<false>{}, identity{})), for_each_result<ranges::dangling, identity>>);
STATIC_ASSERT(same_as<decltype(for_each(borrowed<true>{}, identity{})), for_each_result<int*, identity>>);
// Validate dangling story
STATIC_ASSERT(same_as<decltype(ranges::for_each(borrowed<false>{}, identity{})),
ranges::for_each_result<ranges::dangling, identity>>);
STATIC_ASSERT(
same_as<decltype(ranges::for_each(borrowed<true>{}, identity{})), ranges::for_each_result<int*, identity>>);

R const values = {{{0, 42}, {2, 42}, {4, 42}}};
auto incr = [](auto& y) { ++y; };
struct instantiator {
static constexpr P expected[3] = {{1, 42}, {3, 42}, {5, 42}};

{
auto pairs = values;
auto result = for_each(basic_borrowed_range{pairs}, incr, get_first);
STATIC_ASSERT(same_as<decltype(result), for_each_result<iterator_t<basic_borrowed_range<P>>, decltype(incr)>>);
assert(result.in == basic_borrowed_range{pairs}.end());
int some_value = 1729;
result.fun(some_value);
assert(some_value == 1730);
R const expected = {{{1, 42}, {3, 42}, {5, 42}}};
assert(ranges::equal(pairs, expected));
}
{
auto pairs = values;
basic_borrowed_range wrapped_pairs{pairs};
auto result = for_each(wrapped_pairs.begin(), wrapped_pairs.end(), incr, get_second);
STATIC_ASSERT(same_as<decltype(result), for_each_result<iterator_t<basic_borrowed_range<P>>, decltype(incr)>>);
assert(result.in == wrapped_pairs.end());
int some_value = 1729;
result.fun(some_value);
assert(some_value == 1730);
R const expected = {{{0, 43}, {2, 43}, {4, 43}}};
assert(ranges::equal(pairs, expected));
}
}
template <ranges::input_range ReadWrite>
static constexpr void call() {
using ranges::for_each, ranges::for_each_result, ranges::iterator_t;

int main() {
STATIC_ASSERT((smoke_test(), true));
smoke_test();
}
{ // Validate iterator + sentinel overload
P input[3] = {{0, 42}, {2, 42}, {4, 42}};
ReadWrite wrapped_input{input};

struct instantiator {
template <class In>
static void call(In&& in = {}) {
using I = ranges::iterator_t<In>;
using Fun = void (*)(std::iter_common_reference_t<I>);
ranges::for_each(in, Fun{});
ranges::for_each(ranges::begin(in), ranges::end(in), Fun{});
auto result = for_each(wrapped_input.begin(), wrapped_input.end(), incr, get_first);
STATIC_ASSERT(
same_as<decltype(result), for_each_result<iterator_t<ReadWrite>, remove_const_t<decltype(incr)>>>);
assert(result.in == wrapped_input.end());
assert(ranges::equal(expected, input));

int some_value = 1729;
result.fun(some_value);
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I observe that this test verifies that result.fun is invocable, but not that it actually contains the state accumulated by being invoked on each element. (An incorrect implementation could return a copy of the original callable object.) No change requested.

assert(some_value == 1730);
}
{ // Validate range overload
P input[3] = {{0, 42}, {2, 42}, {4, 42}};
ReadWrite wrapped_input{input};

auto result = for_each(wrapped_input, incr, get_first);
STATIC_ASSERT(
same_as<decltype(result), for_each_result<iterator_t<ReadWrite>, remove_const_t<decltype(incr)>>>);
assert(result.in == wrapped_input.end());
assert(ranges::equal(expected, input));

using ProjFun = void (*)(unique_tag<0>);
ranges::for_each(in, ProjFun{}, ProjectionFor<I>{});
ranges::for_each(ranges::begin(in), ranges::end(in), ProjFun{}, ProjectionFor<I>{});
int some_value = 1729;
result.fun(some_value);
assert(some_value == 1730);
}
}
};

template void test_in<instantiator, const int>();
int main() {
STATIC_ASSERT((test_in<instantiator, P>(), true));
test_in<instantiator, P>();
}
57 changes: 22 additions & 35 deletions tests/std/tests/P0896R4_ranges_alg_for_each_n/test.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -2,53 +2,40 @@
// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception

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

#include <range_algorithm_support.hpp>
using namespace std;
using P = pair<int, int>;

constexpr void smoke_test() {
using ranges::for_each_n, ranges::for_each_n_result, ranges::in_fun_result, ranges::iterator_t;
using std::same_as;
using P = std::pair<int, int>;
using R = std::array<P, 3>;

// Validate that for_each_n_result aliases in_fun_result
STATIC_ASSERT(same_as<for_each_n_result<int, double>, in_fun_result<int, double>>);

R pairs = {{{0, 42}, {2, 42}, {4, 42}}};
auto incr = [](auto& y) { ++y; };

basic_borrowed_range wrapped_pairs{pairs};
auto result = for_each_n(wrapped_pairs.begin(), ranges::distance(pairs), incr, get_first);
STATIC_ASSERT(same_as<decltype(result), for_each_n_result<iterator_t<basic_borrowed_range<P>>, decltype(incr)>>);
assert(result.in == wrapped_pairs.end());
int some_value = 1729;
result.fun(some_value);
assert(some_value == 1730);
R const expected = {{{1, 42}, {3, 42}, {5, 42}}};
assert(ranges::equal(pairs, expected));
}
constexpr auto incr = [](auto& y) { ++y; };

int main() {
STATIC_ASSERT((smoke_test(), true));
smoke_test();
}
// Validate that for_each_n_result aliases in_fun_result
STATIC_ASSERT(same_as<ranges::for_each_n_result<int, double>, ranges::in_fun_result<int, double>>);

struct instantiator {
template <class In>
static void call(In in = {}) {
using std::iter_difference_t;
static constexpr P expected[3] = {{1, 42}, {3, 42}, {5, 42}};

using Fun = void (*)(std::iter_common_reference_t<In>);
ranges::for_each_n(std::move(in), iter_difference_t<In>{}, Fun{});
template <indirectly_writable<P> ReadWrite>
static constexpr void call() {
using ranges::for_each_n, ranges::for_each_n_result, ranges::iterator_t, ranges::distance;
P input[3] = {{0, 42}, {2, 42}, {4, 42}};

using ProjFun = void (*)(unique_tag<0>);
ranges::for_each_n(std::move(in), iter_difference_t<In>{}, ProjFun{}, ProjectionFor<In>{});
auto result = for_each_n(ReadWrite{input}, distance(input), incr, get_first);
STATIC_ASSERT(same_as<decltype(result), for_each_n_result<ReadWrite, remove_const_t<decltype(incr)>>>);
assert(result.in.peek() == end(input));
assert(ranges::equal(expected, input));

int some_value = 1729;
result.fun(some_value);
assert(some_value == 1730);
}
};

template void test_read<instantiator, const int>();
int main() {
STATIC_ASSERT((test_read<instantiator, P>(), true));
test_read<instantiator, P>();
}