This is a follow-up question for A recursive_sum Template Function Implementation with Unwrap Level in C++, A recursive_reduce_all
Template Function Implementation in C++, A recursive_reduce
Template Function with Unwrap Level Implementation in C++ and A recursive_depth Function Implementation with Target Type Parameter in C++. Considering the answer provided by G. Sliepen, std::string
test cases are mentioned. For dealing with these nested std::string
cases, recursive_reduce_string
template function is introduced in this post.
The experimental implementation
recursive_reduce_string
template function implementation// recursive_reduce_string template function template<class T> requires(std::same_as<T, std::string>) constexpr auto recursive_reduce_string(const T& input1) { return input1; } template<std::ranges::input_range T> requires (std::same_as<recursive_unwrap_type_t<recursive_depth<T>() - 1, T>, std::string> && recursive_depth<T>() - 1 == 1) constexpr auto recursive_reduce_string(const T& input) { auto output = input.at(0); for(int i = 1; i < std::ranges::size(input); i++) { output+=input.at(i); } return output; } template<std::ranges::input_range T> constexpr auto recursive_reduce_string(const T& input) { auto result = recursive_reduce_string( UL::recursive_transform<recursive_depth<T>() - 2>( input, [](auto&& element){ return recursive_reduce_string(element); }) ); return result; }
Full Testing Code
The full testing code:
// A `recursive_reduce_string` Template Function Implementation in C++
#include <algorithm>
#include <array>
#include <chrono>
#include <concepts>
#include <deque>
#include <execution>
#include <iostream>
#include <queue>
#include <ranges>
#include <string>
#include <vector>
// is_reservable concept
template<class T>
concept is_reservable = requires(T input)
{
input.reserve(1);
};
// is_sized concept, https://codereview.stackexchange.com/a/283581/231235
template<class T>
concept is_sized = requires(T x)
{
std::size(x);
};
// has_arithmetic_operations concept
template<class T>
concept has_arithmetic_operations = requires(T input)
{
std::plus<>{}(input, input);
std::minus<>{}(input, input);
std::multiplies<>{}(input, input);
std::divides<>{}(input, input);
};
// recursive_depth function implementation
template<typename T>
constexpr std::size_t recursive_depth()
{
return std::size_t{0};
}
template<std::ranges::input_range Range>
constexpr std::size_t recursive_depth()
{
return recursive_depth<std::ranges::range_value_t<Range>>() + std::size_t{1};
}
// recursive_variadic_invoke_result_t implementation
template<std::size_t, typename, typename, typename...>
struct recursive_variadic_invoke_result { };
template<typename F, class...Ts1, template<class...>class Container1, typename... Ts>
struct recursive_variadic_invoke_result<1, F, Container1<Ts1...>, Ts...>
{
using type = Container1<std::invoke_result_t<F,
std::ranges::range_value_t<Container1<Ts1...>>,
std::ranges::range_value_t<Ts>...>>;
};
template<std::size_t unwrap_level, typename F, class...Ts1, template<class...>class Container1, typename... Ts>
requires ( std::ranges::input_range<Container1<Ts1...>> &&
requires { typename recursive_variadic_invoke_result<
unwrap_level - 1,
F,
std::ranges::range_value_t<Container1<Ts1...>>,
std::ranges::range_value_t<Ts>...>::type; }) // The rest arguments are ranges
struct recursive_variadic_invoke_result<unwrap_level, F, Container1<Ts1...>, Ts...>
{
using type = Container1<
typename recursive_variadic_invoke_result<
unwrap_level - 1,
F,
std::ranges::range_value_t<Container1<Ts1...>>,
std::ranges::range_value_t<Ts>...
>::type>;
};
template<std::size_t unwrap_level, typename F, typename T1, typename... Ts>
using recursive_variadic_invoke_result_t = typename recursive_variadic_invoke_result<unwrap_level, F, T1, Ts...>::type;
// recursive_array_invoke_result implementation
template<std::size_t, typename, typename, typename...>
struct recursive_array_invoke_result { };
template< typename F,
template<class, std::size_t> class Container,
typename T,
std::size_t N>
struct recursive_array_invoke_result<1, F, Container<T, N>>
{
using type = Container<
std::invoke_result_t<F, std::ranges::range_value_t<Container<T, N>>>,
N>;
};
template< std::size_t unwrap_level,
typename F,
template<class, std::size_t> class Container,
typename T,
std::size_t N>
requires ( std::ranges::input_range<Container<T, N>> &&
requires { typename recursive_array_invoke_result<
unwrap_level - 1,
F,
std::ranges::range_value_t<Container<T, N>>>::type; }) // The rest arguments are ranges
struct recursive_array_invoke_result<unwrap_level, F, Container<T, N>>
{
using type = Container<
typename recursive_array_invoke_result<
unwrap_level - 1,
F,
std::ranges::range_value_t<Container<T, N>>
>::type, N>;
};
template< std::size_t unwrap_level,
typename F,
template<class, std::size_t> class Container,
typename T,
std::size_t N>
using recursive_array_invoke_result_t = typename recursive_array_invoke_result<unwrap_level, F, Container<T, N>>::type;
// recursive_unwrap_type_t struct implementation, https://codereview.stackexchange.com/q/284610/231235
template<std::size_t, typename, typename...>
struct recursive_unwrap_type { };
template<class...Ts1, template<class...>class Container1, typename... Ts>
struct recursive_unwrap_type<1, Container1<Ts1...>, Ts...>
{
using type = std::ranges::range_value_t<Container1<Ts1...>>;
};
template<std::size_t unwrap_level, class...Ts1, template<class...>class Container1, typename... Ts>
requires ( std::ranges::input_range<Container1<Ts1...>> &&
requires { typename recursive_unwrap_type<
unwrap_level - 1,
std::ranges::range_value_t<Container1<Ts1...>>,
std::ranges::range_value_t<Ts>...>::type; }) // The rest arguments are ranges
struct recursive_unwrap_type<unwrap_level, Container1<Ts1...>, Ts...>
{
using type = typename recursive_unwrap_type<
unwrap_level - 1,
std::ranges::range_value_t<Container1<Ts1...>>
>::type;
};
template<std::size_t unwrap_level, typename T1, typename... Ts>
using recursive_unwrap_type_t = typename recursive_unwrap_type<unwrap_level, T1, Ts...>::type;
// recursive_array_unwrap_type struct implementation, https://stackoverflow.com/a/76347485/6667035
template<std::size_t, typename>
struct recursive_array_unwrap_type { };
template<template<class, std::size_t> class Container,
typename T,
std::size_t N>
struct recursive_array_unwrap_type<1, Container<T, N>>
{
using type = std::ranges::range_value_t<Container<T, N>>;
};
template<std::size_t unwrap_level, template<class, std::size_t> class Container,
typename T,
std::size_t N>
requires ( std::ranges::input_range<Container<T, N>> &&
requires { typename recursive_array_unwrap_type<
unwrap_level - 1,
std::ranges::range_value_t<Container<T, N>>>::type; }) // The rest arguments are ranges
struct recursive_array_unwrap_type<unwrap_level, Container<T, N>>
{
using type = typename recursive_array_unwrap_type<
unwrap_level - 1,
std::ranges::range_value_t<Container<T, N>>
>::type;
};
template<std::size_t unwrap_level, class Container>
using recursive_array_unwrap_type_t = typename recursive_array_unwrap_type<unwrap_level, Container>::type;
// https://codereview.stackexchange.com/a/253039/231235
template<template<class...> class Container = std::vector, std::size_t dim, class T>
constexpr auto n_dim_container_generator(T input, std::size_t times)
{
if constexpr (dim == 0)
{
return input;
}
else
{
return Container(times, n_dim_container_generator<Container, dim - 1, T>(input, times));
}
}
namespace UL // unwrap_level
{
template< std::ranges::input_range Container,
std::copy_constructible F>
requires (std::ranges::view<Container>&&
std::is_object_v<F>)
constexpr auto make_view(const Container& input, const F& f) noexcept
{
return std::ranges::transform_view(
input,
[&f](const auto&& element) constexpr { return recursive_transform(element, f ); } );
}
/* Override make_view to catch dangling references. A borrowed range is
* safe from dangling..
*/
template <std::ranges::input_range T>
requires (!std::ranges::borrowed_range<T>)
constexpr std::ranges::dangling make_view(T&&) noexcept
{
return std::ranges::dangling();
}
// clone_empty_container template function implementation
template< std::size_t unwrap_level = 1,
std::ranges::input_range Container,
std::copy_constructible F>
requires (std::ranges::view<Container>&&
std::is_object_v<F>)
constexpr auto clone_empty_container(const Container& input, const F& f) noexcept
{
const auto view = make_view(input, f);
recursive_variadic_invoke_result<unwrap_level, F, Container> output(std::span{input});
return output;
}
// recursive_transform template function implementation (the version with unwrap_level template parameter)
template< std::size_t unwrap_level = 1,
class T,
std::copy_constructible F,
class Proj = std::identity>
requires (unwrap_level <= recursive_depth<T>()&& // handling incorrect unwrap levels more gracefully, https://codereview.stackexchange.com/a/283563/231235
std::ranges::view<T>&&
std::is_object_v<F>)
constexpr auto recursive_transform(const T& input, const F& f, Proj proj = {} )
{
if constexpr (unwrap_level > 0)
{
auto output = clone_empty_container(input, f);
if constexpr (is_reservable<decltype(output)> &&
is_sized<decltype(input)> &&
std::indirectly_writable<decltype(output),
std::indirect_result_t<F&, std::projected<std::ranges::iterator_t<T>, Proj>>>)
{
output.reserve(input.size());
std::ranges::transform(
input,
std::ranges::begin(output),
[&f](auto&& element) { return recursive_transform<unwrap_level - 1>(element, f); },
proj
);
}
else
{
std::ranges::transform(
input,
std::inserter(output, std::ranges::end(output)),
[&f](auto&& element) { return recursive_transform<unwrap_level - 1>(element, f); },
proj
);
}
return output;
}
else if constexpr(std::regular_invocable<F, T>)
{
return std::invoke(f, std::invoke(proj, input));
}
else
{
static_assert(!std::regular_invocable<F, T>, "Uninvocable?");
}
}
/* This overload of recursive_transform is to support std::array
*/
template< std::size_t unwrap_level = 1,
template<class, std::size_t> class Container,
typename T,
std::size_t N,
typename F >
requires (std::ranges::input_range<Container<T, N>>)
constexpr auto recursive_transform(const Container<T, N>& input, const F& f)
{
recursive_array_invoke_result_t<unwrap_level, F, Container, T, N> output{};
if constexpr (unwrap_level > 1)
{
std::ranges::transform(
input,
std::ranges::begin(output),
[&f](auto&& element) { return recursive_transform<unwrap_level - 1>(element, f); }
);
}
else
{
std::ranges::transform(
input,
std::ranges::begin(output),
f
);
}
return output;
}
// recursive_transform function implementation (the version with unwrap_level, without using view)
template<std::size_t unwrap_level = 1, class T, class F>
requires (unwrap_level <= recursive_depth<T>()&& // handling incorrect unwrap levels more gracefully, https://codereview.stackexchange.com/a/283563/231235
!std::ranges::view<T>)
constexpr auto recursive_transform(const T& input, const F& f)
{
if constexpr (unwrap_level > 0)
{
recursive_variadic_invoke_result_t<unwrap_level, F, T> output{};
std::ranges::transform(
input, // passing a range to std::ranges::transform()
std::inserter(output, std::ranges::end(output)),
[&f](auto&& element) { return recursive_transform<unwrap_level - 1>(element, f); }
);
return output;
}
else
{
return std::invoke(f, input); // use std::invoke()
}
}
// recursive_transform implementation (the version with unwrap_level, with execution policy)
template<std::size_t unwrap_level = 1, class ExPo, class T, std::copy_constructible F>
requires (unwrap_level <= recursive_depth<T>() && // handling incorrect unwrap levels more gracefully, https://codereview.stackexchange.com/a/283563/231235
std::is_execution_policy_v<std::remove_cvref_t<ExPo>>)
constexpr auto recursive_transform(ExPo execution_policy, const T& input, const F& f)
{
if constexpr (unwrap_level > 0)
{
recursive_variadic_invoke_result_t<unwrap_level, F, T> output{};
output.resize(input.size());
std::mutex mutex;
std::transform(execution_policy, std::ranges::cbegin(input), std::ranges::cend(input), std::ranges::begin(output),
[&](auto&& element)
{
std::lock_guard lock(mutex);
return recursive_transform<unwrap_level - 1>(execution_policy, element, f);
});
return output;
}
else
{
return std::invoke(f, input);
}
}
// recursive_transform implementation (binary case, the version with unwrap_level)
template<std::size_t unwrap_level = 1, class ExPo, std::ranges::input_range R1, std::ranges::input_range R2, std::copy_constructible F>
constexpr auto recursive_transform(ExPo execution_policy, const R1& input1, const R2& input2, const F& f)
{
if constexpr (unwrap_level > 0)
{
recursive_variadic_invoke_result_t<unwrap_level, F, R1> output{};
output.resize(input1.size());
std::mutex mutex;
std::transform(execution_policy, std::ranges::cbegin(input1), std::ranges::cend(input1), std::ranges::cbegin(input2), std::ranges::begin(output),
[&](auto&& element1, auto&& element2)
{
std::lock_guard lock(mutex);
return recursive_transform<unwrap_level - 1>(execution_policy, element1, element2, f);
});
return output;
}
else
{
return std::invoke(f, input1, input2);
}
}
}
/* recursive_reduce_all template function performs operation on input container exhaustively
https://codereview.stackexchange.com/a/285831/231235
*/
template<typename T> // No constraint since we're not reducing anything here!
constexpr auto recursive_reduce_all(const T& input)
{
return input;
}
template<std::ranges::input_range T>
requires (has_arithmetic_operations<recursive_unwrap_type_t<recursive_depth<T>(), T>> &&
recursive_depth<T>() == 1)
constexpr auto recursive_reduce_all(const T& input)
{
return std::reduce(std::ranges::cbegin(input), std::ranges::cend(input));
}
// overload for std::array
template<template<class, std::size_t> class Container,
typename T,
std::size_t N>
requires (has_arithmetic_operations<recursive_array_unwrap_type_t<recursive_depth<Container<T, N>>(), Container<T, N>>> &&
recursive_depth<Container<T, N>>() == 1)
constexpr auto recursive_reduce_all(const Container<T, N>& input)
{
return std::reduce(std::ranges::cbegin(input), std::ranges::cend(input));
}
template<std::ranges::input_range T>
requires (has_arithmetic_operations<recursive_unwrap_type_t<recursive_depth<T>(), T>> &&
std::ranges::input_range<recursive_unwrap_type_t<1, T>>)
constexpr auto recursive_reduce_all(const T& input)
{
auto result = recursive_reduce_all(
UL::recursive_transform<recursive_depth<T>() - 1>(input, [](auto&& element){ return recursive_reduce_all(element); })
);
return result;
}
// recursive_reduce_all template function with execution policy
template<class ExPo, has_arithmetic_operations T>
requires (std::is_execution_policy_v<std::remove_cvref_t<ExPo>>)
constexpr auto recursive_reduce_all(ExPo execution_policy, const T& input)
{
return input;
}
template<class ExPo, std::ranges::input_range T>
requires (std::is_execution_policy_v<std::remove_cvref_t<ExPo>> &&
has_arithmetic_operations<recursive_unwrap_type_t<recursive_depth<T>(), T>> &&
recursive_depth<T>() == 1)
constexpr auto recursive_reduce_all(ExPo execution_policy, const T& input)
{
return std::reduce(execution_policy, std::ranges::cbegin(input), std::ranges::cend(input));
}
// recursive_reduce_all template function with execution policy, overload for std::array
template<class ExPo, template<class, std::size_t> class Container,
typename T,
std::size_t N>
requires (std::is_execution_policy_v<std::remove_cvref_t<ExPo>> &&
has_arithmetic_operations<recursive_array_unwrap_type_t<recursive_depth<Container<T, N>>(), Container<T, N>>> &&
recursive_depth<Container<T, N>>() == 1)
constexpr auto recursive_reduce_all(ExPo execution_policy, const Container<T, N>& input)
{
return std::reduce(execution_policy, std::ranges::cbegin(input), std::ranges::cend(input));
}
template<class ExPo, std::ranges::input_range T>
requires (std::is_execution_policy_v<std::remove_cvref_t<ExPo>> &&
has_arithmetic_operations<recursive_unwrap_type_t<recursive_depth<T>(), T>> &&
std::ranges::input_range<recursive_unwrap_type_t<1, T>>)
constexpr auto recursive_reduce_all(ExPo execution_policy, const T& input)
{
auto result = recursive_reduce_all(
UL::recursive_transform<recursive_depth<T>() - 1>(
execution_policy,
input,
[&](auto&& element){ return recursive_reduce_all(execution_policy, element); }
)
);
return result;
}
// recursive_reduce_all template function with initial value
template<has_arithmetic_operations T>
constexpr auto recursive_reduce_all(const T& input1, const T& input2)
{
return input1 + input2;
}
template<std::ranges::input_range T, class TI>
requires (has_arithmetic_operations<recursive_unwrap_type_t<recursive_depth<T>(), T>> &&
std::same_as<recursive_unwrap_type_t<recursive_depth<T>(), T>, TI> &&
recursive_depth<T>() == 1)
constexpr auto recursive_reduce_all(const T& input, TI init)
{
return std::reduce(std::ranges::cbegin(input), std::ranges::cend(input), init);
}
// recursive_reduce_all template function with initial value, overload for std::array
template<template<class, std::size_t> class Container,
typename T,
std::size_t N,
class TI>
requires (has_arithmetic_operations<recursive_array_unwrap_type_t<recursive_depth<Container<T, N>>(), Container<T, N>>> &&
std::same_as<recursive_array_unwrap_type_t<recursive_depth<Container<T, N>>(), Container<T, N>>, TI> &&
recursive_depth<Container<T, N>>() == 1)
constexpr auto recursive_reduce_all(const Container<T, N>& input, TI init)
{
return std::reduce(std::ranges::cbegin(input), std::ranges::cend(input), init);
}
template<std::ranges::input_range T, class TI>
requires (has_arithmetic_operations<recursive_unwrap_type_t<recursive_depth<T>(), T>> &&
std::ranges::input_range<recursive_unwrap_type_t<1, T>> &&
std::same_as<recursive_unwrap_type_t<recursive_depth<T>(), T>, TI>)
constexpr auto recursive_reduce_all(const T& input, TI init)
{
auto result = init + recursive_reduce_all(
UL::recursive_transform<recursive_depth<T>() - 1>(
input,
[&](auto&& element){ return recursive_reduce_all(element); })
);
return result;
}
// recursive_reduce_all template function with execution policy and initial value
template<class ExPo, has_arithmetic_operations T>
requires (std::is_execution_policy_v<std::remove_cvref_t<ExPo>>)
constexpr auto recursive_reduce_all(ExPo execution_policy, const T& input1, const T& input2)
{
return input1 + input2;
}
template<class ExPo, std::ranges::input_range T, class TI>
requires (std::is_execution_policy_v<std::remove_cvref_t<ExPo>> &&
has_arithmetic_operations<recursive_unwrap_type_t<recursive_depth<T>(), T>> &&
std::same_as<recursive_unwrap_type_t<recursive_depth<T>(), T>, TI> &&
recursive_depth<T>() == 1)
constexpr auto recursive_reduce_all(ExPo execution_policy, const T& input, TI init)
{
return std::reduce(execution_policy, std::ranges::cbegin(input), std::ranges::cend(input), init);
}
// recursive_reduce_all template function with execution policy and initial value, overload for std::array
template<class ExPo,
template<class, std::size_t> class Container,
typename T,
std::size_t N,
class TI>
requires (std::is_execution_policy_v<std::remove_cvref_t<ExPo>> &&
has_arithmetic_operations<recursive_array_unwrap_type_t<recursive_depth<Container<T, N>>(), Container<T, N>>> &&
std::same_as<recursive_array_unwrap_type_t<recursive_depth<Container<T, N>>(), Container<T, N>>, TI> &&
recursive_depth<Container<T, N>>() == 1)
constexpr auto recursive_reduce_all(ExPo execution_policy, const Container<T, N>& input, TI init)
{
return std::reduce(execution_policy, std::ranges::cbegin(input), std::ranges::cend(input), init);
}
template<class ExPo, std::ranges::input_range T, class TI>
requires (std::is_execution_policy_v<std::remove_cvref_t<ExPo>> &&
has_arithmetic_operations<recursive_unwrap_type_t<recursive_depth<T>(), T>> &&
std::ranges::input_range<recursive_unwrap_type_t<1, T>> &&
std::same_as<recursive_unwrap_type_t<recursive_depth<T>(), T>, TI>)
constexpr auto recursive_reduce_all(ExPo execution_policy, const T& input, TI init)
{
auto result = init + recursive_reduce_all(
UL::recursive_transform<recursive_depth<T>() - 1>(
execution_policy,
input,
[&](auto&& element){ return recursive_reduce_all(execution_policy, element); })
);
return result;
}
// recursive_reduce_all template function with initial value and specified operation
template<has_arithmetic_operations T, class BinaryOp>
requires (std::regular_invocable<BinaryOp, T, T>)
constexpr auto recursive_reduce_all(const T& input1, const T& input2, BinaryOp binary_op)
{
return std::invoke(binary_op, input1, input2);
}
template<std::ranges::input_range T, class TI, class BinaryOp>
requires (has_arithmetic_operations<recursive_unwrap_type_t<recursive_depth<T>(), T>> &&
std::same_as<recursive_unwrap_type_t<recursive_depth<T>(), T>, TI> &&
recursive_depth<T>() == 1 &&
std::regular_invocable<
BinaryOp,
recursive_unwrap_type_t<recursive_depth<T>(),T>,
recursive_unwrap_type_t<recursive_depth<T>(), T>>
)
constexpr auto recursive_reduce_all(const T& input, TI init, BinaryOp binary_op)
{
return std::reduce(std::ranges::cbegin(input), std::ranges::cend(input), init, binary_op);
}
// recursive_reduce_all template function with initial value and specified operation, overload for std::array
template<template<class, std::size_t> class Container,
typename T,
std::size_t N,
class TI,
class BinaryOp>
requires (has_arithmetic_operations<recursive_array_unwrap_type_t<recursive_depth<Container<T, N>>(), Container<T, N>>> &&
std::same_as<recursive_array_unwrap_type_t<recursive_depth<Container<T, N>>(), Container<T, N>>, TI> &&
recursive_depth<Container<T, N>>() == 1 &&
std::regular_invocable<
BinaryOp,
recursive_array_unwrap_type_t<recursive_depth<Container<T, N>>(), Container<T, N>>,
recursive_array_unwrap_type_t<recursive_depth<Container<T, N>>(), Container<T, N>>>
)
constexpr auto recursive_reduce_all(const Container<T, N>& input, TI init, BinaryOp binary_op)
{
return std::reduce(std::ranges::cbegin(input), std::ranges::cend(input), init, binary_op);
}
template<std::ranges::input_range T, class TI, class BinaryOp>
requires (has_arithmetic_operations<recursive_unwrap_type_t<recursive_depth<T>(), T>> &&
std::ranges::input_range<recursive_unwrap_type_t<1, T>> &&
std::same_as<recursive_unwrap_type_t<recursive_depth<T>(), T>, TI> &&
std::regular_invocable<
BinaryOp,
recursive_unwrap_type_t<recursive_depth<T>(),T>,
recursive_unwrap_type_t<recursive_depth<T>(), T>>
)
constexpr auto recursive_reduce_all(const T& input, TI init, BinaryOp binary_op)
{
auto result = init + recursive_reduce_all(
UL::recursive_transform<recursive_depth<T>() - 1>(
input,
[&](auto&& element){ return recursive_reduce_all(element, init, binary_op); })
);
return result;
}
// recursive_reduce_all template function with execution policy, initial value and specified operation
template<class ExPo, has_arithmetic_operations T, class BinaryOp>
requires (std::is_execution_policy_v<std::remove_cvref_t<ExPo>> &&
std::regular_invocable<BinaryOp, T, T>)
constexpr auto recursive_reduce_all(ExPo execution_policy, const T& input1, const T& input2, BinaryOp binary_op)
{
return std::invoke(binary_op, input1, input2);
}
template<class ExPo, std::ranges::input_range T, class TI, class BinaryOp>
requires (std::is_execution_policy_v<std::remove_cvref_t<ExPo>> &&
has_arithmetic_operations<recursive_unwrap_type_t<recursive_depth<T>(), T>> &&
std::same_as<recursive_unwrap_type_t<recursive_depth<T>(), T>, TI> &&
recursive_depth<T>() == 1 &&
std::regular_invocable<
BinaryOp,
recursive_unwrap_type_t<recursive_depth<T>(),T>,
recursive_unwrap_type_t<recursive_depth<T>(), T>>
)
constexpr auto recursive_reduce_all(ExPo execution_policy, const T& input, TI init, BinaryOp binary_op)
{
return std::reduce(execution_policy, std::ranges::cbegin(input), std::ranges::cend(input), init, binary_op);
}
// recursive_reduce_all template function with execution policy, initial value and specified operation, overload for std::array
template<class ExPo,
template<class, std::size_t> class Container,
typename T,
std::size_t N,
class TI, class BinaryOp>
requires (std::is_execution_policy_v<std::remove_cvref_t<ExPo>> &&
has_arithmetic_operations<recursive_array_unwrap_type_t<recursive_depth<Container<T, N>>(), Container<T, N>>> &&
std::same_as<recursive_array_unwrap_type_t<recursive_depth<Container<T, N>>(), Container<T, N>>, TI> &&
recursive_depth<Container<T, N>>() == 1 &&
std::regular_invocable<
BinaryOp,
recursive_array_unwrap_type_t<recursive_depth<Container<T, N>>(), Container<T, N>>,
recursive_array_unwrap_type_t<recursive_depth<Container<T, N>>(), Container<T, N>>>
)
constexpr auto recursive_reduce_all(ExPo execution_policy, const Container<T, N>& input, TI init, BinaryOp binary_op)
{
return std::reduce(execution_policy, std::ranges::cbegin(input), std::ranges::cend(input), init, binary_op);
}
template<class ExPo, std::ranges::input_range T, class TI, class BinaryOp>
requires (std::is_execution_policy_v<std::remove_cvref_t<ExPo>> &&
has_arithmetic_operations<recursive_unwrap_type_t<recursive_depth<T>(), T>> &&
std::ranges::input_range<recursive_unwrap_type_t<1, T>> &&
std::same_as<recursive_unwrap_type_t<recursive_depth<T>(), T>, TI> &&
std::regular_invocable<
BinaryOp,
recursive_unwrap_type_t<recursive_depth<T>(),T>,
recursive_unwrap_type_t<recursive_depth<T>(), T>>
)
constexpr auto recursive_reduce_all(ExPo execution_policy, const T& input, TI init, BinaryOp binary_op)
{
auto result = init + recursive_reduce_all(
UL::recursive_transform<recursive_depth<T>() - 1>(
execution_policy,
input,
[&](auto&& element){ return recursive_reduce_all(execution_policy, element, init, binary_op); })
);
return result;
}
template<std::size_t wrapped_level = 0, class T>
constexpr auto get_wrapped_first_element(const T& input)
{
if constexpr (wrapped_level > 0)
{
return get_wrapped_first_element<wrapped_level - 1>(input.at(0));
}
else
{
return input;
}
}
// recursive_reduce_string template function
template<class T>
requires(std::same_as<T, std::string>)
constexpr auto recursive_reduce_string(const T& input1)
{
return input1;
}
template<std::ranges::input_range T>
requires (std::same_as<recursive_unwrap_type_t<recursive_depth<T>() - 1, T>, std::string> &&
recursive_depth<T>() - 1 == 1)
constexpr auto recursive_reduce_string(const T& input)
{
auto output = input.at(0);
for(int i = 1; i < std::ranges::size(input); i++)
{
output+=input.at(i);
}
return output;
}
template<std::ranges::input_range T>
constexpr auto recursive_reduce_string(const T& input)
{
auto result = recursive_reduce_string(
UL::recursive_transform<recursive_depth<T>() - 2>(
input,
[](auto&& element){ return recursive_reduce_string(element); })
);
return result;
}
void recursive_reduce_string_tests()
{
std::cout << "Play with std::vectors:\n";
std::vector<std::string> word_vector1 = {"foo", "bar", "baz", "quux"};
std::cout << recursive_reduce_string(word_vector1) << '\n';
std::vector<std::vector<std::string>> word_vector2 = {word_vector1, word_vector1, word_vector1};
std::cout << recursive_reduce_string(word_vector2) << '\n';
std::cout << "Play with std::deque:\n";
std::deque<std::string> word_deque1 = {"1", "2", "3", "4"};
std::cout << recursive_reduce_string(word_deque1) << '\n';
std::deque<std::deque<std::string>> word_deque2 = {word_deque1, word_deque1, word_deque1};
std::cout << recursive_reduce_string(word_deque2) << '\n';
return;
}
int main()
{
auto start = std::chrono::system_clock::now();
recursive_reduce_string_tests();
auto end = std::chrono::system_clock::now();
std::chrono::duration<double> elapsed_seconds = end - start;
std::time_t end_time = std::chrono::system_clock::to_time_t(end);
std::cout << "Computation finished at " << std::ctime(&end_time) << "elapsed time: " << elapsed_seconds.count() << '\n';
return 0;
}
The output of the test code above:
Play with std::vectors:
foobarbazquux
foobarbazquuxfoobarbazquuxfoobarbazquux
Play with std::deque:
1234
123412341234
Computation finished at Tue Oct 17 08:31:04 2023
elapsed time: 0.00128089
All suggestions are welcome.
The summary information:
Which question it is a follow-up to?
A recursive_sum Template Function Implementation with Unwrap Level in C++,
A
recursive_reduce_all
Template Function Implementation in C++,A
recursive_reduce
Template Function with Unwrap Level Implementation in C++ andA recursive_depth Function Implementation with Target Type Parameter in C++.
What changes has been made in the code since last question?
For dealing with these nested
std::string
cases,recursive_reduce_string
template function is introduced in this post.Why a new review is being asked for?
Please review
recursive_reduce_string
template function implementation and all suggestions are welcome.
#include
s? \$\endgroup\$