Skip to content

✨ Add for_each_butlast and initial_medial_final #215

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

Merged
merged 1 commit into from
Apr 14, 2025
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
55 changes: 53 additions & 2 deletions docs/algorithm.adoc
Original file line number Diff line number Diff line change
Expand Up @@ -14,23 +14,74 @@ variadic in its inputs.
----
template <typename InputIt, typename Operation, typename... InputItN>
constexpr auto for_each(InputIt first, InputIt last,
Operation op, InputItN... first_n) -> Operation;
Operation op, InputItN... first_n)
-> for_each_result<Operation, InputItN...>;
----

The return value is equivalent to a `tuple<Operation, InputItN...>`.
In C\\++20 and later this is a `stdx::tuple`, in C++17 a `std::tuple`.

NOTE: `stdx::for_each` is `constexpr` in C++20 and later, because it uses
https://en.cppreference.com/w/cpp/utility/functional/invoke[`std::invoke`].

`stdx::for_each_n` is just like `stdx::for_each`, but instead of taking two
iterators to delimit the input range, it takes an iterator and size.
Correspondingly, its return value includes the advanced `InputIt`.

[source,cpp]
----
template <typename InputIt, typename Size, typename Operation,
typename... InputItN>
constexpr auto for_each_n(InputIt first, Size n,
Operation op, InputItN... first_n) -> Operation;
Operation op, InputItN... first_n);
-> for_each_result<Operation, InputIt, InputItN...>;
----

=== `for_each_butlast`, `for_each_butlastn`

`stdx::for_each_butlast` is like `for_each` but omits the last element of
each range.

[source,cpp]
----
template <typename FwdIt, typename Operation,
typename... FwdItN>
constexpr auto for_each_butlast(FwdIt first, FwdIt last,
Operation op, FwdItN... first_n)
-> for_each_result<Operation, FwdIt, FwdItN...>;
----

`stdx::for_each_butlastn` omits the last `n` elements of each range.

[source,cpp]
----
template <typename FwdIt, typename Size, typename Operation,
typename... FwdItN>
constexpr auto for_each_butlastn(FwdIt first, FwdIt last, N n,
Operation op, FwdItN... first_n)
-> for_each_result<Operation, FwdIt, FwdItN...>;
----

NOTE: `for_each_butlast` and `for_each_butlastn` are defined for forward
iterators (or stronger) only.

=== `initial_medial_final`

`initial_medial_final` iterates over a range, calling one function
for the initial element, another function for each of the medial elements, and a third
function for the final element.

[source,cpp]
----
template <typename FwdIt, typename IOp, typename MOp, typename FOp>
CONSTEXPR_INVOKE auto initial_medial_final(FwdIt first, FwdIt last, IOp iop,
MOp mop, FOp fop)
-> for_each_result<IOp, MOp, FOp>;
----

If the range is only two elements, there are no medial elements, so `mop` is not
called. If the range is only one element, `fop` is not called. And if the range
is empty, no functions are called.

=== `transform` and `transform_n`

Expand Down
75 changes: 71 additions & 4 deletions include/stdx/algorithm.hpp
Original file line number Diff line number Diff line change
@@ -1,11 +1,13 @@
#pragma once

#include <functional>
#include <iterator>
#if __cplusplus >= 202002L
#include <stdx/tuple.hpp>
#else
#include <tuple>
#endif
#include <type_traits>

namespace stdx {
inline namespace v1 {
Expand Down Expand Up @@ -48,25 +50,90 @@ CONSTEXPR_INVOKE auto transform_n(InputIt first, Size n, OutputIt d_first,
return {d_first, first, first_n...};
}

template <typename Op, typename... Is>
using for_each_result = detail::result_tuple_t<Op, Is...>;

template <typename InputIt, typename Operation, typename... InputItN>
CONSTEXPR_INVOKE auto for_each(InputIt first, InputIt last, Operation op,
InputItN... first_n) -> Operation {
InputItN... first_n)
-> for_each_result<Operation, InputItN...> {
while (first != last) {
std::invoke(op, *first, *first_n...);
static_cast<void>(++first), (static_cast<void>(++first_n), ...);
}
return op;
return {op, first_n...};
}

template <typename InputIt, typename Size, typename Operation,
typename... InputItN>
CONSTEXPR_INVOKE auto for_each_n(InputIt first, Size n, Operation op,
InputItN... first_n) -> Operation {
InputItN... first_n)
-> for_each_result<Operation, InputIt, InputItN...> {
while (n-- > 0) {
std::invoke(op, *first, *first_n...);
static_cast<void>(++first), (static_cast<void>(++first_n), ...);
}
return op;
return {op, first, first_n...};
}

namespace detail {
template <typename FwdIt, typename N, typename Operation, typename... FwdItN>
CONSTEXPR_INVOKE auto for_each_butlastn(std::forward_iterator_tag, FwdIt first,
FwdIt last, N n, Operation op,
FwdItN... first_n)
-> for_each_result<Operation, FwdIt, FwdItN...> {
auto adv_it = first;
for (auto i = N{}; i < n; ++i) {
if (adv_it == last) {
break;
}
++adv_it;
}

while (adv_it != last) {
std::invoke(op, *first, *first_n...);
static_cast<void>(++first), (static_cast<void>(++first_n), ...);
++adv_it;
}
return {op, first, first_n...};
}

template <typename RandIt, typename N, typename Operation, typename... RandItN>
CONSTEXPR_INVOKE auto for_each_butlastn(std::random_access_iterator_tag,
RandIt first, RandIt last, N n,
Operation op, RandItN... first_n) {
auto const sz = std::distance(first, last);
return for_each_n(first, sz - static_cast<decltype(sz)>(n), op, first_n...);
}
} // namespace detail

template <typename FwdIt, typename N, typename Operation, typename... FwdItN>
CONSTEXPR_INVOKE auto for_each_butlastn(FwdIt first, FwdIt last, N n,
Operation op, FwdItN... first_n) {
return detail::for_each_butlastn(
typename std::iterator_traits<FwdIt>::iterator_category{}, first, last,
n, op, first_n...);
}

template <typename FwdIt, typename Operation, typename... FwdItN>
CONSTEXPR_INVOKE auto for_each_butlast(FwdIt first, FwdIt last, Operation op,
FwdItN... first_n) {
return for_each_butlastn(first, last, 1, op, first_n...);
}

template <typename FwdIt, typename IOp, typename MOp, typename FOp>
CONSTEXPR_INVOKE auto initial_medial_final(FwdIt first, FwdIt last, IOp iop,
MOp mop, FOp fop)
-> for_each_result<IOp, MOp, FOp> {
if (first != last) {
iop(*first);
auto [op, it] = for_each_butlast(++first, last, mop);
if (it != last) {
fop(*it);
}
return {iop, op, fop};
}
return {iop, mop, fop};
}

#undef CONSTEXPR_INVOKE
Expand Down
7 changes: 3 additions & 4 deletions include/stdx/tuple.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -105,11 +105,10 @@ template <std::size_t Index, typename T, typename... Ts> struct element {
T value;

private:
[[nodiscard]] friend constexpr auto operator==(element const &,
element const &)
[[nodiscard]] friend auto operator==(element const &x, element const &y)
-> bool = default;
[[nodiscard]] friend constexpr auto operator<=>(element const &,
element const &) = default;
[[nodiscard]] friend auto operator<=>(element const &,
element const &) = default;
};

template <typename Op, typename Value> struct fold_helper {
Expand Down
Loading