2
\$\begingroup\$

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 implementation

    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);
    }
    
  • recursive_flatten_view template function implementation with unwrap_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.

Godbolt link is here.

All suggestions are welcome.

The summary information:

asked Apr 21 at 16:15
\$\endgroup\$

0

Know someone who can answer? Share a link to this question via email, Twitter, or Facebook.

Your Answer

Draft saved
Draft discarded

Sign up or log in

Sign up using Google
Sign up using Email and Password

Post as a guest

Required, but never shown

Post as a guest

Required, but never shown

By clicking "Post Your Answer", you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.