This is a follow-up question for A recursive_minmax Template Function Implementation in C++ and A recursive_flatten_view Template Function Implementation in C++. The recursive_minmax
template function with unwrap_level
is implemented in this post.
The experimental implementation
recursive_minmax
template function implementationtemplate<std::size_t unwrap_level, std::ranges::forward_range R, class Proj = std::identity, std::indirect_strict_weak_order< std::projected<std::ranges::iterator_t<R>, Proj>> Comp = std::ranges::less> requires std::indirectly_copyable_storable<std::ranges::iterator_t<R>, std::ranges::range_value_t<R>*> constexpr auto recursive_minmax(R&& numbers, Comp comp = {}, Proj proj = {}) { return std::ranges::minmax(impl::recursive_flatten_view<unwrap_level>(numbers), comp, proj); }
recursive_flatten_view
template function implementation withunwrap_level
// recursive_flatten_view template function implementation with unwrap level template<std::size_t unwrap_level, typename T> static std::generator<const recursive_unwrap_type_t<unwrap_level, T>&> recursive_flatten_view(const T& input) { if constexpr (unwrap_level > 0) { for (const auto& element : input) for (const auto& value : recursive_flatten_view<unwrap_level - 1>(element)) co_yield value; } else { co_yield input; } }
Full Testing Code
The full testing code:
// A recursive_minmax Template Function with Unwrap Level Implementation in C++
#include <algorithm>
#include <array>
#include <cassert>
#include <chrono>
#include <concepts>
#include <coroutine>
#include <deque>
#include <execution>
#if __cplusplus >= 202302L || _HAS_CXX23
#include <generator>
#endif
#include <iostream>
#include <list>
#include <numeric> // for std::reduce
#include <ranges>
#include <vector>
// recursive_unwrap_type_t struct implementation
template<std::size_t, typename, typename...>
struct recursive_unwrap_type { };
template<class T>
struct recursive_unwrap_type<0, T>
{
using type = T;
};
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_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};
}
struct recursive_print_fn
{
template<std::ranges::input_range T>
constexpr auto operator()(const T& input, const std::size_t level = 0) const
{
T output = input;
std::cout << std::string(level, ' ') << "Level " << level << ":\n";
std::ranges::transform(std::ranges::cbegin(input), std::ranges::cend(input), std::ranges::begin(output),
[&](auto&& x)
{
std::cout << std::string(level, ' ') << x << std::endl;
return x;
}
);
return output;
}
template<std::ranges::input_range T>
requires (std::ranges::input_range<std::ranges::range_value_t<T>>)
constexpr auto operator()(const T& input, const std::size_t level = 0) const
{
T output = input;
std::cout << std::string(level, ' ') << "Level " << level << ":\n";
std::ranges::transform(std::ranges::cbegin(input), std::ranges::cend(input), std::ranges::begin(output),
[&](auto&& element)
{
return operator()(element, level + 1);
}
);
return output;
}
};
inline constexpr recursive_print_fn recursive_print;
bool comp(int a, int b){
return a > b;
}
template<std::size_t dim, class T>
constexpr auto n_dim_vector_generator(T input, std::size_t times)
{
if constexpr (dim == 0)
{
return input;
}
else
{
auto element = n_dim_vector_generator<dim - 1>(input, times);
std::vector<decltype(element)> output(times, element);
return output;
}
}
template<std::size_t dim, std::size_t times, class T>
constexpr auto n_dim_array_generator(T input)
{
if constexpr (dim == 0)
{
return input;
}
else
{
auto element = n_dim_array_generator<dim - 1, times>(input);
std::array<decltype(element), times> output;
std::fill(std::ranges::begin(output), std::ranges::end(output), element);
return output;
}
}
template<std::size_t dim, class T>
constexpr auto n_dim_deque_generator(T input, std::size_t times)
{
if constexpr (dim == 0)
{
return input;
}
else
{
auto element = n_dim_deque_generator<dim - 1>(input, times);
std::deque<decltype(element)> output(times, element);
return output;
}
}
template<std::size_t dim, class T>
constexpr auto n_dim_list_generator(T input, std::size_t times)
{
if constexpr (dim == 0)
{
return input;
}
else
{
auto element = n_dim_list_generator<dim - 1>(input, times);
std::list<decltype(element)> output(times, element);
return output;
}
}
template<std::size_t dim, class T, template<class...> class Container = std::vector>
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<dim - 1, T, Container>(input, times));
}
}
#if __cplusplus >= 202302L || _HAS_CXX23
namespace impl {
// recursive_flatten_view template function implementation with unwrap level
template<std::size_t unwrap_level, typename T>
static std::generator<const recursive_unwrap_type_t<unwrap_level, T>&> recursive_flatten_view(const T& input)
{
if constexpr (unwrap_level > 0)
{
for (const auto& element : input)
for (const auto& value : recursive_flatten_view<unwrap_level - 1>(element))
co_yield value;
}
else
{
co_yield input;
}
}
}
#endif
template<std::size_t unwrap_level, std::ranges::forward_range R, class Proj = std::identity,
std::indirect_strict_weak_order<
std::projected<std::ranges::iterator_t<R>, Proj>> Comp = std::ranges::less>
requires std::indirectly_copyable_storable<std::ranges::iterator_t<R>, std::ranges::range_value_t<R>*>
constexpr auto recursive_minmax(R&& numbers, Comp comp = {}, Proj proj = {})
{
return std::ranges::minmax(impl::recursive_flatten_view<unwrap_level>(numbers), comp, proj);
}
// From https://stackoverflow.com/a/37264642/6667035
#ifndef NDEBUG
# define M_Assert(Expr, Msg) \
M_Assert_Helper(#Expr, Expr, __FILE__, __LINE__, Msg)
#else
# define M_Assert(Expr, Msg) ;
#endif
void M_Assert_Helper(const char* expr_str, bool expr, const char* file, int line, std::string msg)
{
if (!expr)
{
std::cerr << "Assert failed:\t" << msg << "\n"
<< "Expected:\t" << expr_str << "\n"
<< "Source:\t\t" << file << ", line " << line << "\n";
abort();
}
}
void recursive_minmax_test()
{
auto test_vector = n_dim_container_generator<3>(3, 3);
test_vector.at(0).at(0).at(0) = 5;
test_vector.at(0).at(0).at(1) = -5;
auto [min_number, max_number] = recursive_minmax<3>(test_vector);
M_Assert(
max_number == 5,
"recursive_minmax test case failed");
M_Assert(
min_number == -5,
"recursive_minmax test case failed");
return;
}
class Timer
{
private:
std::chrono::system_clock::time_point start, end;
std::chrono::duration<double> elapsed_seconds;
std::time_t end_time;
public:
Timer()
{
start = std::chrono::system_clock::now();
}
~Timer()
{
end = std::chrono::system_clock::now();
elapsed_seconds = end - start;
end_time = std::chrono::system_clock::to_time_t(end);
if (elapsed_seconds.count() != 1)
{
std::print(std::cout, "Computation finished at {} elapsed time: {} seconds.\n", std::ctime(&end_time), elapsed_seconds.count());
}
else
{
std::print(std::cout, "Computation finished at {} elapsed time: {} second.\n", std::ctime(&end_time), elapsed_seconds.count());
}
}
};
int main()
{
Timer timer1;
recursive_minmax_test();
return EXIT_SUCCESS;
}
The output of the test code above:
Computation finished at Mon Apr 21 15:58:08 2025
elapsed time: 0.000942601 seconds.
All suggestions are welcome.
The summary information:
Which question it is a follow-up to?
A recursive_minmax Template Function Implementation in C++ and
A recursive_flatten_view Template Function Implementation in C++
What changes has been made in the code since last question?
The
recursive_minmax
template function withunwrap_level
is implemented in this post.Why a new review is being asked for?
If there is any possible improvement, please let me know.