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.