Created
June 4, 2025 11:44
-
-
Save davidspry/06b58281da6133e2def5a4b452736318 to your computer and use it in GitHub Desktop.
Generic reduction functions amenable to inlining
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| #pragma once | |
| #include <concepts> | |
| #include <cstddef> | |
| #include <functional> | |
| #include <type_traits> | |
| #include <utility> | |
| /// Right-fold reduce N successive invocations of the given object. | |
| /// | |
| /// @details | |
| /// Equivalent to (reduce(fn(), reduce(..., reduce(fn(), fn())))) | |
| /// | |
| /// @tparam N | |
| /// The number of invocations | |
| /// | |
| /// @tparam Rd | |
| /// A binary reduction function | |
| /// | |
| /// @example | |
| /// fold_right<N, std::plus<float>>([&] { return next_sample(); }); | |
| template<size_t N, class BinaryReduce, std::invocable Fn> | |
| requires (N != 0) && std::is_invocable_v<BinaryReduce, std::invoke_result_t<Fn>, std::invoke_result_t<Fn>> | |
| constexpr auto fold_right(Fn&& fn) { | |
| return [&, reduce = BinaryReduce{}]<size_t K>(this auto&& self, auto&& operand) requires (K <= N) { | |
| if constexpr (K == N) return std::move(operand); | |
| if constexpr (K != N) return std::invoke(reduce, std::move(operand), | |
| self.template operator()<K + 1>(std::invoke(fn)) | |
| ); | |
| }.template operator()<1>(std::invoke(fn)); | |
| } | |
| /// Right-fold reduce N successive invocations of the given object, | |
| /// instantiatings its operator() template with the loop index | |
| /// for each invocation. | |
| /// | |
| /// @details | |
| /// Equivalent to (reduce(fn<0>(), reduce(..., reduce(fn<N-2>(), fn<N-1>())))) | |
| /// | |
| /// @tparam N | |
| /// The number of invocations | |
| /// | |
| /// @tparam Reducer | |
| /// A binary reduction function | |
| /// | |
| /// @example | |
| /// fold_right<N, std::plus<unsigned>>([]<size_t K> { return Coefficient<K>::value; }); | |
| template<size_t N, class BinaryReduce, class Fn> | |
| requires (N != 0) && | |
| requires { std::declval<Fn>().template operator()<N>(); } && | |
| requires { std::declval<BinaryReduce>()(std::declval<Fn>().template operator()<N>(), std::declval<Fn>().template operator()<N>()); } | |
| constexpr auto fold_right(Fn&& fn) { | |
| return [&, reduce = BinaryReduce{}]<size_t K>(this auto&& self, auto&& operand) requires (K <= N) { | |
| if constexpr (K == N) return std::move(operand); | |
| if constexpr (K != N) return std::invoke(reduce, std::move(operand), | |
| self.template operator()<K + 1>(fn.template operator()<K>()) | |
| ); | |
| }.template operator()<1>(fn.template operator()<0>()); | |
| } | |
| /// Left-fold reduce N successive invocations of the given object. | |
| /// | |
| /// @details | |
| /// Equivalent to (reduce(...reduce(reduce(fn(), fn()), fn()), ..., fn())) | |
| /// | |
| /// @tparam N | |
| /// The number of invocations | |
| /// | |
| /// @tparam Rd | |
| /// A binary reduction function | |
| /// | |
| /// @example | |
| /// fold_left<N, std::plus<float>>([&] { return next_sample(); }); | |
| template<size_t N, class BinaryReduce, std::invocable Fn> | |
| requires (N != 0) && std::is_invocable_v<BinaryReduce, std::invoke_result_t<Fn>, std::invoke_result_t<Fn>> | |
| constexpr auto fold_left(Fn&& fn) { | |
| return [&, reduce = BinaryReduce{}]<size_t K>(this auto&& self, auto&& operand) requires (K <= N) { | |
| if constexpr (K == N) return std::move(operand); | |
| if constexpr (K != N) return self.template operator()<K + 1>( | |
| std::invoke(reduce, std::move(operand), std::invoke(fn)) | |
| ); | |
| }.template operator()<1>(std::invoke(fn)); | |
| } | |
| /// Left-fold reduce N successive invocations of the given object, | |
| /// instantiatings its operator() template with the loop index | |
| /// for each invocation. | |
| /// | |
| /// @details | |
| /// Equivalent to (reduce(...reduce(reduce(fn<0>(), fn<1>()), fn<2>()), ..., fn<N-1>())) | |
| /// | |
| /// @tparam N | |
| /// The number of invocations | |
| /// | |
| /// @tparam Reducer | |
| /// A binary reduction function | |
| /// | |
| /// @example | |
| /// fold_left<N, std::multiplies<float>>([this]<size_t K> { return std::get<K>(m_factors); }); | |
| template<size_t N, class BinaryReduce, class Fn> | |
| requires (N != 0) && | |
| requires { std::declval<Fn>().template operator()<N>(); } && | |
| requires { std::declval<BinaryReduce>()(std::declval<Fn>().template operator()<N>(), std::declval<Fn>().template operator()<N>()); } | |
| constexpr auto fold_left(Fn&& fn) { | |
| return [&, reduce = BinaryReduce{}]<size_t K>(this auto&& self, auto&& operand) requires (K <= N) { | |
| if constexpr (K == N) return std::move(operand); | |
| if constexpr (K != N) return self.template operator()<K + 1>( | |
| std::invoke(reduce, std::move(operand), fn.template operator()<K>()) | |
| ); | |
| }.template operator()<1>(fn.template operator()<0>()); | |
| } |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment