This is a follow-up question for A recursive_find_if Template Function with Unwrap Level Implementation in C++ and recursive_invocable and recursive_project_invocable Concept Implementation in C++. I am trying to implement recursive_find
and recursive_find_if_not
template functions in this post.
The experimental implementation
recursive_find
Template Functiontemplate<std::size_t unwrap_level, class R, class T, class Proj = std::identity> requires(recursive_invocable<unwrap_level, Proj, R>) constexpr auto recursive_find(R&& range, T&& target, Proj&& proj = {}) { if constexpr (unwrap_level) { return std::ranges::find_if(range, [&](auto& element) { return recursive_find<unwrap_level - 1>(element, target, proj); }) != std::ranges::end(range); } else { return range == std::invoke(proj, target); } }
recursive_find
Template Function with Execution Policytemplate<std::size_t unwrap_level, class ExecutionPolicy, class R, class T, class Proj = std::identity> requires(recursive_invocable<unwrap_level, Proj, R>&& std::is_execution_policy_v<std::remove_cvref_t<ExecutionPolicy>>) constexpr auto recursive_find(ExecutionPolicy execution_policy, R&& range, T&& target, Proj&& proj = {}) { if constexpr (unwrap_level) { return std::find_if(execution_policy, std::ranges::begin(range), std::ranges::end(range), [&](auto& element) { return recursive_find<unwrap_level - 1>(execution_policy, element, target, proj); }) != std::ranges::end(range); } else { return range == std::invoke(proj, target); } }
recursive_find_if_not
Template Function/* recursive_find_if_not template function implementation with unwrap level */ template<std::size_t unwrap_level, class T, class Proj = std::identity, recursive_projected_invocable<unwrap_level, Proj, T> UnaryPredicate> constexpr auto recursive_find_if_not(T&& value, UnaryPredicate&& p, Proj&& proj = {}) { if constexpr (unwrap_level > 0) { return std::ranges::find_if(value, [&](auto& element) { return recursive_find_if_not<unwrap_level - 1>(element, p, proj); }) != std::ranges::end(value); } else { return !std::invoke(p, std::invoke(proj, value)); } }
Full Testing Code
The full testing code:
// recursive_find and recursive_find_if_not Template Functions Implementation in C++ #include <algorithm> #include <array> #include <cassert> #include <chrono> #include <concepts> #include <deque> #include <execution> #include <exception> //#include <experimental/ranges/algorithm> #include <experimental/array> #include <functional> #include <iostream> #include <iterator> #include <ranges> #include <string> #include <utility> #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); }; template<typename T> concept is_summable = requires(T x) { x + x; }; // 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_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_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<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)); } } 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 { std::array<decltype(n_dim_array_generator<dim - 1, times>(input)), times> output; for (size_t i = 0; i < times; i++) { output[i] = n_dim_array_generator<dim - 1, times>(input); } return output; } } // 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_depth function implementation with target type template<typename T_Base, typename T> constexpr std::size_t recursive_depth() { return std::size_t{0}; } template<typename T_Base, std::ranges::input_range Range> requires (!std::same_as<Range, T_Base>) constexpr std::size_t recursive_depth() { return recursive_depth<T_Base, std::ranges::range_value_t<Range>>() + std::size_t{1}; } // is_recursive_invocable template function implementation template<std::size_t unwrap_level, class F, class... T> requires(unwrap_level <= recursive_depth<T...>()) static constexpr bool is_recursive_invocable() { if constexpr (unwrap_level == 0) { return std::invocable<F, T...>; } else { return is_recursive_invocable< unwrap_level - 1, F, std::ranges::range_value_t<T>...>(); } } // recursive_invocable concept template<std::size_t unwrap_level, class F, class... T> concept recursive_invocable = is_recursive_invocable<unwrap_level, F, T...>(); // is_recursive_project_invocable template function implementation template<std::size_t unwrap_level, class Proj, class F, class... T> requires(unwrap_level <= recursive_depth<T...>() && recursive_invocable<unwrap_level, Proj, T...>) static constexpr bool is_recursive_project_invocable() { if constexpr (unwrap_level == 0) { return std::invocable<F, std::invoke_result_t<Proj, T...>>; } else { return is_recursive_project_invocable< unwrap_level - 1, Proj, F, std::ranges::range_value_t<T>...>(); } } // recursive_project_invocable concept template<class F, std::size_t unwrap_level, class Proj, class... T> concept recursive_projected_invocable = is_recursive_project_invocable<unwrap_level, Proj, F, T...>(); /* recursive_all_of template function implementation with unwrap level */ template<std::size_t unwrap_level, class T, class Proj = std::identity, recursive_projected_invocable<unwrap_level, Proj, T> UnaryPredicate> constexpr auto recursive_all_of(T&& value, UnaryPredicate&& p, Proj&& proj = {}) { if constexpr (unwrap_level > 0) { return std::ranges::all_of(value, [&](auto&& element) { return recursive_all_of<unwrap_level - 1>(element, p, proj); }); } else { return std::invoke(p, std::invoke(proj, value)); } } /* recursive_find template function implementation with unwrap level */ template<std::size_t unwrap_level, class R, class T, class Proj = std::identity> requires(recursive_invocable<unwrap_level, Proj, R>) constexpr auto recursive_find(R&& range, T&& target, Proj&& proj = {}) { if constexpr (unwrap_level) { return std::ranges::find_if(range, [&](auto& element) { return recursive_find<unwrap_level - 1>(element, target, proj); }) != std::ranges::end(range); } else { return range == std::invoke(proj, target); } } template<std::size_t unwrap_level, class ExecutionPolicy, class R, class T, class Proj = std::identity> requires(recursive_invocable<unwrap_level, Proj, R>&& std::is_execution_policy_v<std::remove_cvref_t<ExecutionPolicy>>) constexpr auto recursive_find(ExecutionPolicy execution_policy, R&& range, T&& target, Proj&& proj = {}) { if constexpr (unwrap_level) { return std::find_if(execution_policy, std::ranges::begin(range), std::ranges::end(range), [&](auto& element) { return recursive_find<unwrap_level - 1>(execution_policy, element, target, proj); }) != std::ranges::end(range); } else { return range == std::invoke(proj, target); } } /* recursive_find_if template function implementation with unwrap level */ template<std::size_t unwrap_level, class T, class Proj = std::identity, recursive_projected_invocable<unwrap_level, Proj, T> UnaryPredicate> constexpr auto recursive_find_if(T&& value, UnaryPredicate&& p, Proj&& proj = {}) { if constexpr (unwrap_level > 0) { return std::ranges::find_if(value, [&](auto& element) { return recursive_find_if<unwrap_level - 1>(element, p, proj); }) != std::ranges::end(value); } else { return std::invoke(p, std::invoke(proj, value)); } } /* recursive_find_if_not template function implementation with unwrap level */ template<std::size_t unwrap_level, class T, class Proj = std::identity, recursive_projected_invocable<unwrap_level, Proj, T> UnaryPredicate> constexpr auto recursive_find_if_not(T&& value, UnaryPredicate&& p, Proj&& proj = {}) { if constexpr (unwrap_level > 0) { return std::ranges::find_if(value, [&](auto& element) { return recursive_find_if_not<unwrap_level - 1>(element, p, proj); }) != std::ranges::end(value); } else { return !std::invoke(p, std::invoke(proj, value)); } } // recursive_any_of template function implementation with unwrap level template<std::size_t unwrap_level, class T, class Proj = std::identity, recursive_projected_invocable<unwrap_level, Proj, T> UnaryPredicate> constexpr auto recursive_any_of(T&& value, UnaryPredicate&& p, Proj&& proj = {}) { return recursive_find_if<unwrap_level>(value, p, proj); } // recursive_none_of template function implementation with unwrap level template<std::size_t unwrap_level, class T, class Proj = std::identity, recursive_projected_invocable<unwrap_level, Proj, T> UnaryPredicate> constexpr auto recursive_none_of(T&& value, UnaryPredicate&& p, Proj&& proj = {}) { return !recursive_any_of<unwrap_level>(value, p, proj); } template<std::ranges::input_range T> constexpr auto recursive_print(const T& input, const int level = 0) { T output = input; std::cout << std::string(level, ' ') << "Level " << level << ":" << std::endl; std::transform(input.cbegin(), input.cend(), output.begin(), [level](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 T recursive_print(const T& input, const int level = 0) { 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), [level](auto&& element) { return recursive_print(element, level + 1); } ); return output; } void recursive_find_tests() { auto test_vectors_1 = n_dim_container_generator<4, int, std::vector>(1, 3); test_vectors_1[0][0][0][0] = 2; assert(recursive_find<4>(test_vectors_1, 2)); assert(recursive_find<4>(std::execution::par, test_vectors_1, 2)); auto test_vectors_2 = n_dim_container_generator<4, int, std::vector>(3, 3); assert(recursive_find<4>(test_vectors_2, 2) == false); assert(recursive_find<4>(test_vectors_2, 3)); assert(recursive_find<4>(test_vectors_2, 4) == false); // Tests with std::string auto test_vector_string = n_dim_container_generator<4, std::string, std::vector>("1", 3); assert(recursive_find<4>(test_vector_string, "1")); assert(recursive_find<4>(test_vector_string, "2") == false); // Tests with std::string, projection assert(recursive_find<4>( test_vector_string, "1", [](auto&& element) {return std::to_string(std::stoi(element) + 1); }) == false); // Tests with std::array of std::string std::array<std::string, 3> word_array1 = {"foo", "foo", "foo"}; assert(recursive_find<1>(word_array1, "foo")); assert(recursive_find<1>(word_array1, "bar") == false); // Tests with std::deque of std::string std::deque<std::string> word_deque1 = {"foo", "foo", "foo", "bar"}; assert(recursive_find<1>(word_deque1, "foo")); assert(recursive_find<1>(word_deque1, "bar")); assert(recursive_find<1>(word_deque1, "abcd") == false); assert(recursive_find<2>(word_deque1, 'a')); assert(recursive_find<2>(word_deque1, 'b')); assert(recursive_find<2>(word_deque1, 'c') == false); std::vector<std::wstring> wstring_vector1{}; for(int i = 0; i < 4; ++i) { wstring_vector1.push_back(std::to_wstring(1)); } assert(recursive_find<1>(wstring_vector1, std::to_wstring(1))); assert(recursive_find<1>(wstring_vector1, std::to_wstring(2)) == false); std::vector<std::u8string> u8string_vector1{}; for(int i = 0; i < 4; ++i) { u8string_vector1.push_back(u8"\u20AC2.00"); } assert(recursive_find<1>(u8string_vector1, u8"\u20AC2.00")); assert(recursive_find<1>(u8string_vector1, u8"\u20AC1.00") == false); std::pmr::string pmr_string1 = "123"; std::vector<std::pmr::string> pmr_string_vector1 = {pmr_string1, pmr_string1, pmr_string1}; assert(recursive_find<1>(pmr_string_vector1, "123")); assert(recursive_find<1>(pmr_string_vector1, "456") == false); std::cout << "All tests passed!\n"; return; } void recursive_find_if_not_tests() { auto test_vectors_1 = n_dim_container_generator<4, int, std::vector>(1, 3); test_vectors_1[0][0][0][0] = 2; assert(recursive_find_if_not<4>(test_vectors_1, [](auto&& i) { return i == 1; })); auto test_vectors_2 = n_dim_container_generator<4, int, std::vector>(3, 3); assert(recursive_find_if_not<4>(test_vectors_2, [](auto&& i) { return i == 3; }) == false); // Tests with std::string auto test_vector_string = n_dim_container_generator<4, std::string, std::vector>("1", 3); assert(recursive_find_if_not<4>(test_vector_string, [](auto&& i) { return i == "1"; }) == false); assert(recursive_find_if_not<4>(test_vector_string, [](auto&& i) { return i == "2"; })); assert(recursive_find_if_not<4>(test_vector_string, [](auto&& i) { return i == "3"; })); // Tests with std::string, projection assert(recursive_find_if_not<4>( test_vector_string, [](auto&& i) { return i == "1"; }, [](auto&& element) {return std::to_string(std::stoi(element) + 1); })); assert(recursive_find_if_not<4>( test_vector_string, [](auto&& i) { return i == "2"; }, [](auto&& element) {return std::to_string(std::stoi(element) + 1); }) == false); // Tests with std::array of std::string std::array<std::string, 3> word_array1 = {"foo", "foo", "foo"}; assert(recursive_find_if_not<1>(word_array1, [](auto&& i) { return i == "foo"; }) == false); assert(recursive_find_if_not<1>(word_array1, [](auto&& i) { return i == "bar"; })); std::cout << "All tests passed!\n"; return; } int main() { auto start = std::chrono::system_clock::now(); recursive_find_tests(); recursive_find_if_not_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 EXIT_SUCCESS; }
The output of the test code above:
All tests passed! All tests passed! Computation finished at Sat Feb 24 02:56:36 2024 elapsed time: 0.00221348
All suggestions are welcome.
The summary information:
Which question it is a follow-up to?
A recursive_find_if Template Function with Unwrap Level Implementation in C++ and
recursive_invocable and recursive_project_invocable Concept Implementation in C++
What changes has been made in the code since last question?
I am trying to implement
recursive_find
andrecursive_find_if_not
template functions in this post.Why a new review is being asked for?
Please review the implementation of
recursive_find
andrecursive_find_if_not
template functions.