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

Folding algorithms #3099

Merged
merged 43 commits into from
Oct 24, 2022
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
43 commits
Select commit Hold shift + click to select a range
35bde4c
Add `in_value_result`
JMazurkiewicz Sep 10, 2022
b58023e
Add concepts from the paper
JMazurkiewicz Sep 10, 2022
4582ca2
Add feature test macro
JMazurkiewicz Sep 12, 2022
077d532
Initial implementation of `ranges::fold*`
JMazurkiewicz Sep 12, 2022
c11d6a2
Add missing test coverage for `ranges::in_value_result`
JMazurkiewicz Sep 12, 2022
3fa441b
Change return type of `_Fold_left_with_iter_unchecked`
JMazurkiewicz Sep 12, 2022
21a44eb
Test coverage cleanup
JMazurkiewicz Sep 12, 2022
fdca2fe
Check `ranges::dangling` in call to `ranges::fold_left[_first]_with_i…
JMazurkiewicz Sep 12, 2022
ef7dbc6
Test more folds with empty ranges
JMazurkiewicz Sep 12, 2022
d8fe8db
Rename variables in tests: `resN` -> `sumN`
JMazurkiewicz Sep 12, 2022
8c56ffc
Rename variables in tests: `(left/right)_folded_sum` -> `(left/right)…
JMazurkiewicz Sep 12, 2022
728f92d
Test multiplication in `fold_right(_first)` tests
JMazurkiewicz Sep 12, 2022
20ee9df
Test subtraction in `fold_left_first(_with_iter)` tests
JMazurkiewicz Sep 12, 2022
2fd84c4
A little bit better test of `ranges::in_value_result`
JMazurkiewicz Sep 12, 2022
f168505
Add test coverage to `test.lst`
JMazurkiewicz Sep 12, 2022
7ee8a1b
Move test comment in `yvals_core.h`
JMazurkiewicz Sep 12, 2022
9e27501
Add `empty_rng` variable in tests
JMazurkiewicz Sep 12, 2022
df4a3cb
Tests: `vector<double>` -> `const vector<double>` (+ last tweaks befo…
JMazurkiewicz Sep 12, 2022
1684bdf
Make `_Fold_right_last_unchecked` invoke `_Fold_right_unchecked` dire…
JMazurkiewicz Sep 13, 2022
932f78a
`__cpp_lib_fold` -> `__cpp_lib_ranges_fold`
JMazurkiewicz Sep 14, 2022
623df07
Use `static_cast` and `_Ubegin`
JMazurkiewicz Sep 14, 2022
727ca6d
Create temporary `_Init_ref` variable
JMazurkiewicz Sep 14, 2022
7ea2809
Include `<optional>` only in C++23 mode
JMazurkiewicz Sep 15, 2022
fc7741f
Fix comment in `yvals_core.h`
JMazurkiewicz Sep 15, 2022
216fede
Make `fold_left` and `fold_left_with_iter(R)` call `fold_left_with_it…
JMazurkiewicz Sep 15, 2022
9abf727
Make `fold_left_first` and `fold_left_first_with_iter(R)` call `fold_…
JMazurkiewicz Sep 15, 2022
bd996f3
Make `_Fold_left_(first_)with_iter` functions private members
JMazurkiewicz Sep 15, 2022
2a767da
Example of what I mean
strega-nil Sep 15, 2022
b660eec
really should have run build/tests
strega-nil Sep 15, 2022
f161da3
Merge remote-tracking branch 'upstream/main' into JMazurkiewicz/fold
strega-nil Sep 15, 2022
e60d090
Merge branch 'main' into fold
JMazurkiewicz Sep 19, 2022
d5ef5a6
_EXPORT_STD!
JMazurkiewicz Sep 19, 2022
14b6c75
Rename `in_value_result`'s template arguments (for consistency)
JMazurkiewicz Sep 20, 2022
bb1c947
Merge branch 'main' into fold
JMazurkiewicz Oct 15, 2022
ca43eac
Update formatting
JMazurkiewicz Oct 15, 2022
e0e9de8
Update formatting (again)
JMazurkiewicz Oct 15, 2022
5dab66e
Remove `clang-format [off/on]` comments
JMazurkiewicz Oct 15, 2022
5fcedeb
Include <utility> for move().
StephanTLavavej Oct 20, 2022
10a9b3b
Fix typos: fold_left => fold_right.
StephanTLavavej Oct 20, 2022
d7b7bd2
Add _RANGES qualification.
StephanTLavavej Oct 20, 2022
ca4cc6f
Use static_cast<iter_value_t<_It>> to avoid functional-style casts.
StephanTLavavej Oct 20, 2022
d505bcd
Use braces to construct optional.
StephanTLavavej Oct 20, 2022
5c57818
Merge branch 'main' into fold
StephanTLavavej Oct 24, 2022
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
271 changes: 271 additions & 0 deletions stl/inc/algorithm
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,10 @@
#if _STL_COMPILER_PREPROCESSOR
#include <xmemory>

#if _HAS_CXX23
#include <optional>
#endif // _HAS_CXX23

#pragma pack(push, _CRT_PACKING)
#pragma warning(push, _STL_WARNING_LEVEL)
#pragma warning(disable : _STL_DISABLED_WARNINGS)
Expand Down Expand Up @@ -210,6 +214,26 @@ namespace ranges {
return {_STD move(in), found};
}
};

#if _HAS_CXX23
_EXPORT_STD template <class _In, class _Ty>
struct in_value_result {
/* [[no_unique_address]] */ _In in;
/* [[no_unique_address]] */ _Ty value;

template <class _IIn, class _TTy>
requires convertible_to<const _In&, _IIn> && convertible_to<const _Ty&, _TTy>
constexpr operator in_value_result<_IIn, _TTy>() const& {
return {in, value};
}

template <class _IIn, class _TTy>
requires convertible_to<_In, _IIn> && convertible_to<_Ty, _TTy>
constexpr operator in_value_result<_IIn, _TTy>() && {
return {_STD move(in), _STD move(value)};
}
};
#endif // _HAS_CXX23
} // namespace ranges
#endif // __cpp_lib_concepts

Expand Down Expand Up @@ -2512,6 +2536,253 @@ namespace ranges {
};

_EXPORT_STD inline constexpr _Ends_with_fn ends_with{_Not_quite_object::_Construct_tag{}};

template <class _Fn>
class _Flipped {
private:
_Fn _Func;

public:
template <class _Ty, class _Uty>
requires invocable<_Fn&, _Uty, _Ty>
invoke_result_t<_Fn&, _Uty, _Ty> operator()(_Ty&&, _Uty&&);
};

template <class _Fn, class _Ty, class _It, class _Uty>
concept _Indirectly_binary_left_foldable_impl =
movable<_Ty> && movable<_Uty> && convertible_to<_Ty, _Uty> && invocable<_Fn&, _Uty, iter_reference_t<_It>>
&& assignable_from<_Uty&, invoke_result_t<_Fn&, _Uty, iter_reference_t<_It>>>;

template <class _Fn, class _Ty, class _It>
concept _Indirectly_binary_left_foldable =
copy_constructible<_Fn> && indirectly_readable<_It> && invocable<_Fn&, _Ty, iter_reference_t<_It>>
&& convertible_to<invoke_result_t<_Fn&, _Ty, iter_reference_t<_It>>,
decay_t<invoke_result_t<_Fn&, _Ty, iter_reference_t<_It>>>>
&& _Indirectly_binary_left_foldable_impl<_Fn, _Ty, _It,
decay_t<invoke_result_t<_Fn&, _Ty, iter_reference_t<_It>>>>;

template <class _Fn, class _Ty, class _It>
concept _Indirectly_binary_right_foldable = _Indirectly_binary_left_foldable<_Flipped<_Fn>, _Ty, _It>;

_EXPORT_STD template <class _It, class _Ty>
using fold_left_with_iter_result = in_value_result<_It, _Ty>;

_EXPORT_STD template <class _It, class _Ty>
using fold_left_first_with_iter_result = in_value_result<_It, _Ty>;

class _Fold_left_with_iter_fn : private _Not_quite_object {
public:
using _Not_quite_object::_Not_quite_object;

template <input_iterator _It, sentinel_for<_It> _Se, class _Ty, _Indirectly_binary_left_foldable<_Ty, _It> _Fn>
_NODISCARD constexpr auto operator()(_It _First, _Se _Last, _Ty _Init, _Fn _Func) const {
_Adl_verify_range(_First, _Last);
return _Fold_left_with_iter_impl<_It>(
_STD move(_First), _STD move(_Last), _STD move(_Init), _Pass_fn(_Func));
}

template <input_range _Rng, class _Ty, _Indirectly_binary_left_foldable<_Ty, iterator_t<_Rng>> _Fn>
_NODISCARD constexpr auto operator()(_Rng&& _Range, _Ty _Init, _Fn _Func) const {
return _Fold_left_with_iter_impl<borrowed_iterator_t<_Rng>>(
_RANGES begin(_Range), _RANGES end(_Range), _STD move(_Init), _Pass_fn(_Func));
}

private:
template <class _RetIt, class _It, class _Se, class _Ty, class _Fn>
_NODISCARD constexpr auto _Fold_left_with_iter_impl(_It&& _First, _Se&& _Last, _Ty&& _Init, _Fn _Func) const {
_STL_INTERNAL_STATIC_ASSERT(input_iterator<_It>);
_STL_INTERNAL_STATIC_ASSERT(sentinel_for<_Se, _It>);
_STL_INTERNAL_STATIC_ASSERT(_Indirectly_binary_left_foldable<_Fn, _Ty, _It>);

using _Uty = decay_t<invoke_result_t<_Fn&, _Ty, iter_reference_t<_It>>>;
using _Return_type = fold_left_with_iter_result<_RetIt, _Uty>;

if (_First == _Last) {
return _Return_type{_STD move(_First), static_cast<_Uty>(_STD move(_Init))};
} else {
auto _UFirst = _Unwrap_iter<_Se>(_STD move(_First));
auto _ULast = _Unwrap_sent<_It>(_STD move(_Last));

_Uty _Accum = _STD invoke(_Func, _STD move(_Init), *_UFirst);
for (++_UFirst; _UFirst != _ULast; ++_UFirst) {
_Accum = _STD invoke(_Func, _STD move(_Accum), *_UFirst);
}

_Seek_wrapped(_First, _STD move(_UFirst));
return _Return_type{_STD move(_First), _STD move(_Accum)};
}
}
};

_EXPORT_STD inline constexpr _Fold_left_with_iter_fn fold_left_with_iter{_Not_quite_object::_Construct_tag{}};

class _Fold_left_fn : private _Not_quite_object {
public:
using _Not_quite_object::_Not_quite_object;

template <input_iterator _It, sentinel_for<_It> _Se, class _Ty, _Indirectly_binary_left_foldable<_Ty, _It> _Fn>
_NODISCARD constexpr auto operator()(_It _First, _Se _Last, _Ty _Init, _Fn _Func) const {
return _RANGES fold_left_with_iter(_STD move(_First), _Last, _STD move(_Init), _Pass_fn(_Func)).value;
}

template <input_range _Rng, class _Ty, _Indirectly_binary_left_foldable<_Ty, iterator_t<_Rng>> _Fn>
_NODISCARD constexpr auto operator()(_Rng&& _Range, _Ty _Init, _Fn _Func) const {
return _RANGES fold_left_with_iter(_STD forward<_Rng>(_Range), _STD move(_Init), _Pass_fn(_Func)).value;
Copy link
Contributor

Choose a reason for hiding this comment

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

Ideally we'd want this to avoid the extra move into the in_value_result and back out. (This is why the wording uses Returns: instead of "Effects: Equivalent to return ...;")

Copy link
Member

Choose a reason for hiding this comment

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

Do RVO or deferred temporary materialization help with that?

I think this would be reasonable to address in a followup PR.

}
};

_EXPORT_STD inline constexpr _Fold_left_fn fold_left{_Not_quite_object::_Construct_tag{}};

class _Fold_left_first_with_iter_fn : private _Not_quite_object {
public:
using _Not_quite_object::_Not_quite_object;

template <input_iterator _It, sentinel_for<_It> _Se,
_Indirectly_binary_left_foldable<iter_value_t<_It>, _It> _Fn>
requires constructible_from<iter_value_t<_It>, iter_reference_t<_It>>
_NODISCARD constexpr auto operator()(_It _First, _Se _Last, _Fn _Func) const {
_Adl_verify_range(_First, _Last);
return _Fold_left_first_with_iter_impl<_It>(_STD move(_First), _STD move(_Last), _Pass_fn(_Func));
}

template <input_range _Rng, _Indirectly_binary_left_foldable<range_value_t<_Rng>, iterator_t<_Rng>> _Fn>
requires constructible_from<range_value_t<_Rng>, range_reference_t<_Rng>>
_NODISCARD constexpr auto operator()(_Rng&& _Range, _Fn _Func) const {
return _Fold_left_first_with_iter_impl<borrowed_iterator_t<_Rng>>(
_RANGES begin(_Range), _RANGES end(_Range), _Pass_fn(_Func));
}

private:
template <class _RetIt, class _It, class _Se, class _Fn>
_NODISCARD constexpr auto _Fold_left_first_with_iter_impl(_It&& _First, _Se&& _Last, _Fn _Func) const {
_STL_INTERNAL_STATIC_ASSERT(input_iterator<_It>);
_STL_INTERNAL_STATIC_ASSERT(sentinel_for<_Se, _It>);
_STL_INTERNAL_STATIC_ASSERT(_Indirectly_binary_left_foldable<_Fn, iter_value_t<_It>, _It>);
_STL_INTERNAL_STATIC_ASSERT(constructible_from<iter_value_t<_It>, iter_reference_t<_It>>);

using _Uty =
decltype(_RANGES fold_left(_STD move(_First), _Last, static_cast<iter_value_t<_It>>(*_First), _Func));
using _Return_type = fold_left_first_with_iter_result<_RetIt, optional<_Uty>>;
if (_First == _Last) {
return _Return_type{_STD move(_First), optional<_Uty>{}};
} else {
auto _UFirst = _Unwrap_iter<_Se>(_STD move(_First));
auto _ULast = _Unwrap_sent<_It>(_STD move(_Last));

optional<_Uty> _Init{in_place, *_UFirst};
_Uty& _Init_ref = *_Init;
for (++_UFirst; _UFirst != _ULast; ++_UFirst) {
_Init_ref = _STD invoke(_Func, _STD move(_Init_ref), *_UFirst);
}

_Seek_wrapped(_First, _STD move(_UFirst));
return _Return_type{_STD move(_First), _STD move(_Init)};
}
}
};

_EXPORT_STD inline constexpr _Fold_left_first_with_iter_fn fold_left_first_with_iter{
_Not_quite_object::_Construct_tag{}};

class _Fold_left_first_fn : private _Not_quite_object {
public:
using _Not_quite_object::_Not_quite_object;

template <input_iterator _It, sentinel_for<_It> _Se,
_Indirectly_binary_left_foldable<iter_value_t<_It>, _It> _Fn>
requires constructible_from<iter_value_t<_It>, iter_reference_t<_It>>
_NODISCARD constexpr auto operator()(_It _First, _Se _Last, _Fn _Func) const {
return _RANGES fold_left_first_with_iter(_STD move(_First), _STD move(_Last), _Pass_fn(_Func)).value;
}

template <input_range _Rng, _Indirectly_binary_left_foldable<range_value_t<_Rng>, iterator_t<_Rng>> _Fn>
requires constructible_from<range_value_t<_Rng>, range_reference_t<_Rng>>
_NODISCARD constexpr auto operator()(_Rng&& _Range, _Fn _Func) const {
return _RANGES fold_left_first_with_iter(_STD forward<_Rng>(_Range), _Pass_fn(_Func)).value;
}
};

_EXPORT_STD inline constexpr _Fold_left_first_fn fold_left_first{_Not_quite_object::_Construct_tag{}};

template <class _It, class _Se, class _Ty, class _Fn>
_NODISCARD constexpr auto _Fold_right_unchecked(_It _First, _Se _Last, _Ty _Init, _Fn _Func) {
_STL_INTERNAL_STATIC_ASSERT(bidirectional_iterator<_It>);
_STL_INTERNAL_STATIC_ASSERT(sentinel_for<_Se, _It>);
_STL_INTERNAL_STATIC_ASSERT(_Indirectly_binary_right_foldable<_Fn, _Ty, _It>);

using _Uty = decay_t<invoke_result_t<_Fn&, iter_reference_t<_It>, _Ty>>;
if (_First == _Last) {
return static_cast<_Uty>(_STD move(_Init));
} else {
_It _Tail = _RANGES next(_First, _Last);
_Uty _Accum = _STD invoke(_Func, *--_Tail, _STD move(_Init));
while (_First != _Tail) {
_Accum = _STD invoke(_Func, *--_Tail, _STD move(_Accum));
}
return _Accum;
}
}

class _Fold_right_fn : private _Not_quite_object {
public:
using _Not_quite_object::_Not_quite_object;

template <bidirectional_iterator _It, sentinel_for<_It> _Se, class _Ty,
_Indirectly_binary_right_foldable<_Ty, _It> _Fn>
_NODISCARD constexpr auto operator()(_It _First, _Se _Last, _Ty _Init, _Fn _Func) const {
_Adl_verify_range(_First, _Last);
return _Fold_right_unchecked(_Unwrap_iter<_Se>(_STD move(_First)), _Unwrap_sent<_It>(_STD move(_Last)),
_STD move(_Init), _Pass_fn(_Func));
}

template <bidirectional_range _Rng, class _Ty, _Indirectly_binary_right_foldable<_Ty, iterator_t<_Rng>> _Fn>
_NODISCARD constexpr auto operator()(_Rng&& _Range, _Ty _Init, _Fn _Func) const {
return _Fold_right_unchecked(_Ubegin(_Range), _Uend(_Range), _STD move(_Init), _Pass_fn(_Func));
}
};

_EXPORT_STD inline constexpr _Fold_right_fn fold_right{_Not_quite_object::_Construct_tag{}};

class _Fold_right_last_fn : private _Not_quite_object {
public:
using _Not_quite_object::_Not_quite_object;

template <bidirectional_iterator _It, sentinel_for<_It> _Se,
_Indirectly_binary_right_foldable<iter_value_t<_It>, _It> _Fn>
requires constructible_from<iter_value_t<_It>, iter_reference_t<_It>>
_NODISCARD constexpr auto operator()(_It _First, _Se _Last, _Fn _Func) const {
_Adl_verify_range(_First, _Last);
return _Fold_right_last_unchecked(
_Unwrap_iter<_Se>(_STD move(_First)), _Unwrap_sent<_It>(_STD move(_Last)), _Pass_fn(_Func));
}

template <bidirectional_range _Rng,
_Indirectly_binary_right_foldable<range_value_t<_Rng>, iterator_t<_Rng>> _Fn>
requires constructible_from<range_value_t<_Rng>, range_reference_t<_Rng>>
_NODISCARD constexpr auto operator()(_Rng&& _Range, _Fn _Func) const {
return _Fold_right_last_unchecked(_Ubegin(_Range), _Uend(_Range), _Pass_fn(_Func));
}

private:
template <class _It, class _Se, class _Fn>
_NODISCARD constexpr auto _Fold_right_last_unchecked(_It _First, _Se _Last, _Fn _Func) const {
_STL_INTERNAL_STATIC_ASSERT(bidirectional_iterator<_It>);
_STL_INTERNAL_STATIC_ASSERT(sentinel_for<_Se, _It>);
_STL_INTERNAL_STATIC_ASSERT(_Indirectly_binary_right_foldable<_Fn, iter_value_t<_It>, _It>);
_STL_INTERNAL_STATIC_ASSERT(constructible_from<iter_value_t<_It>, iter_reference_t<_It>>);

using _Uty = decltype(_RANGES fold_right(_First, _Last, static_cast<iter_value_t<_It>>(*_First), _Func));
if (_First == _Last) {
return optional<_Uty>{};
} else {
_It _Tail = _RANGES prev(_RANGES next(_First, _STD move(_Last)));
return optional<_Uty>{in_place, _RANGES _Fold_right_unchecked(_STD move(_First), _Tail,
static_cast<iter_value_t<_It>>(*_Tail), _STD move(_Func))};
}
}
};

_EXPORT_STD inline constexpr _Fold_right_last_fn fold_right_last{_Not_quite_object::_Construct_tag{}};
#endif // _HAS_CXX23
} // namespace ranges
#endif // __cpp_lib_concepts
Expand Down
2 changes: 2 additions & 0 deletions stl/inc/yvals_core.h
Original file line number Diff line number Diff line change
Expand Up @@ -330,6 +330,7 @@
// P2302R4 ranges::contains, ranges::contains_subrange
// P2321R2 zip
// (missing views::zip_transform, views::adjacent, and views::adjacent_transform)
// P2322R6 ranges::fold_left, ranges::fold_right, Etc.
// P2387R3 Pipe Support For User-Defined Range Adaptors
// P2417R2 More constexpr bitset
// P2438R2 string::substr() &&
Expand Down Expand Up @@ -1655,6 +1656,7 @@ _EMIT_STL_ERROR(STL1004, "C++98 unexpected() is incompatible with C++23 unexpect
#define __cpp_lib_ranges_chunk 202202L
#define __cpp_lib_ranges_chunk_by 202202L
#define __cpp_lib_ranges_contains 202207L
#define __cpp_lib_ranges_fold 202207L
#define __cpp_lib_ranges_iota 202202L
#define __cpp_lib_ranges_join_with 202202L
#define __cpp_lib_ranges_slide 202202L
Expand Down
1 change: 1 addition & 0 deletions tests/std/test.lst
Original file line number Diff line number Diff line change
Expand Up @@ -552,6 +552,7 @@ tests\P2302R4_ranges_alg_contains
tests\P2302R4_ranges_alg_contains_subrange
tests\P2321R2_proxy_reference
tests\P2321R2_views_zip
tests\P2322R6_ranges_alg_fold
tests\P2387R3_bind_back
tests\P2387R3_pipe_support_for_user_defined_range_adaptors
tests\P2401R0_conditional_noexcept_for_exchange
Expand Down
4 changes: 4 additions & 0 deletions tests/std/tests/P2322R6_ranges_alg_fold/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_latest_matrix.lst
Loading