This is a follow-up question for A recursive_flatten Template Function Implementation in C++. I am trying to follow G. Sliepen's answer to create recursive_flatten_view
template function for iterating over a nested container. I know that all unnecessary copy operations should be avoided, but I am not sure the way which I've done is correct. If there is any possible improvement, please let me know.
The experimental implementation
recursive_flatten_view
template function implementationnamespace impl { struct recursive_flatten_view_fn { template<std::ranges::range Container> requires (std::ranges::range<std::ranges::range_value_t<Container>>) constexpr auto operator()(const Container& input) const { recursive_unwrap_type_t<recursive_depth<Container>() - 1, Container> output_container; return std::views::all(recursive_flatten(input, output_container)); } }; inline constexpr recursive_flatten_view_fn recursive_flatten_view; }
recursive_flatten
template function implementationnamespace impl { struct recursive_flatten_fn { // recursive_flatten template function implementation template<std::ranges::range T, class OutputContainer> constexpr auto operator()(const T& input, OutputContainer output_container) const { output_container.append_range(input); return output_container; } template<std::ranges::range Container, class OutputContainer> requires (std::ranges::range<std::ranges::range_value_t<Container>>) constexpr auto operator()(const Container& input, OutputContainer output_container) const { for (const auto& element : input) { output_container = operator()(element, output_container); } return output_container; } }; inline constexpr recursive_flatten_fn recursive_flatten; }
recursive_minmax
template function implementation: The usage example ofrecursive_flatten_view
template function.template<std::ranges::input_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(numbers), comp, proj); }
Full Testing Code
The full testing code:
// A recursive_flatten_view Template Function Implementation in C++
#include <algorithm>
#include <array>
#include <cassert>
#include <chrono>
#include <concepts>
#include <deque>
#include <execution>
#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...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 int level = 0) const
{
T output = input;
std::cout << std::string(level, ' ') << "Level " << level << ":" << std::endl;
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 int level = 0) const
{
T output = input;
std::cout << std::string(level, ' ') << "Level " << level << ":" << std::endl;
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));
}
}
namespace impl {
struct recursive_flatten_fn
{
// recursive_flatten template function implementation
template<std::ranges::range T, class OutputContainer>
constexpr auto operator()(const T& input, OutputContainer output_container) const
{
output_container.append_range(input);
return output_container;
}
template<std::ranges::range Container, class OutputContainer>
requires (std::ranges::range<std::ranges::range_value_t<Container>>)
constexpr auto operator()(const Container& input, OutputContainer output_container) const
{
for (const auto& element : input) {
output_container = operator()(element, output_container);
}
return output_container;
}
};
inline constexpr recursive_flatten_fn recursive_flatten;
struct recursive_flatten_view_fn
{
template<std::ranges::range Container>
requires (std::ranges::range<std::ranges::range_value_t<Container>>)
constexpr auto operator()(const Container& input) const
{
recursive_unwrap_type_t<recursive_depth<Container>() - 1, Container> output_container;
return std::views::all(recursive_flatten(input, output_container));
}
};
inline constexpr recursive_flatten_view_fn recursive_flatten_view;
}
template<std::ranges::input_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(numbers), comp, proj);
}
template<std::ranges::input_range Container>
constexpr auto recursive_reduce_all(const Container& input)
{
auto flattened_view = impl::recursive_flatten_view(input);
return std::reduce(std::ranges::cbegin(flattened_view), std::ranges::cend(flattened_view));
}
template<class ExPo, std::ranges::input_range Container>
requires (std::is_execution_policy_v<std::remove_cvref_t<ExPo>>)
constexpr auto recursive_reduce_all(ExPo execution_policy, const Container& input)
{
auto flattened_view = impl::recursive_flatten_view(input);
return std::reduce(execution_policy, std::ranges::cbegin(flattened_view), std::ranges::cend(flattened_view));
}
template<std::ranges::input_range Container, class InitT>
constexpr auto recursive_reduce_all(const Container& input, InitT init)
{
auto flattened_input = impl::recursive_flatten_view(input);
return std::reduce(std::ranges::cbegin(flattened_input), std::ranges::cend(flattened_input), init);
}
template<class ExPo, std::ranges::input_range Container, class InitT>
requires (std::is_execution_policy_v<std::remove_cvref_t<ExPo>>)
constexpr auto recursive_reduce_all(ExPo execution_policy, const Container& input, InitT init)
{
auto flattened_input = impl::recursive_flatten_view(input);
return std::reduce(execution_policy, std::ranges::cbegin(flattened_input), std::ranges::cend(flattened_input), init);
}
template<std::ranges::input_range Container, class InitT, class BinaryOp>
constexpr auto recursive_reduce_all(const Container& input, InitT init, BinaryOp op)
{
auto flattened_input = impl::recursive_flatten_view(input);
return std::reduce(std::ranges::cbegin(flattened_input), std::ranges::cend(flattened_input), init, op);
}
template<class ExPo, std::ranges::input_range Container, class InitT, class BinaryOp>
requires (std::is_execution_policy_v<std::remove_cvref_t<ExPo>>)
constexpr auto recursive_reduce_all(ExPo execution_policy, const Container& input, InitT init, BinaryOp op)
{
auto flattened_input = impl::recursive_flatten_view(input);
return std::reduce(execution_policy, std::ranges::cbegin(flattened_input), std::ranges::cend(flattened_input), init, op);
}
// Copy from https://stackoverflow.com/a/37264642/6667035
#ifndef NDEBUG
# define M_Assert(Expr, Msg) \
__M_Assert(#Expr, Expr, __FILE__, __LINE__, Msg)
#else
# define M_Assert(Expr, Msg) ;
#endif
void __M_Assert(const char* expr_str, bool expr, const char* file, int line, const char* 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_flatten_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(test_vector);
M_Assert(
max_number == 5,
"recursive_minmax test case failed");
M_Assert(
min_number == -5,
"recursive_minmax test case failed");
M_Assert(
recursive_reduce_all(test_vector) == 75,
"recursive_reduce_all test case failed");
}
int main()
{
auto start = std::chrono::system_clock::now();
recursive_flatten_test();
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:
Computation finished at Sat Apr 27 10:25:54 2024
elapsed time: 3e-05
All suggestions are welcome.
The summary information:
Which question it is a follow-up to?
What changes has been made in the code since last question?
I am trying to create
recursive_flatten_view
template function in this post.Why a new review is being asked for?
If there is any possible improvement, please let me know.