This is a follow-up question for Avoiding requires clause if possible on a series recursive function in C++ and A recursive_count_if Function with Automatic Type Deducing from Lambda for Various Type Arbitrary Nested Iterable Implementation in C++. Thanks to indi's detailed and clear answer. I got the key point "The point is to consider what kind of type your algorithm is supposed to work for. It’s about SEMANTICS, not syntax." and I am trying to perform the suggestion ideas including "using standard concepts wherever possible" and "avoiding the unnecessary copies" on function recursive_count_if based on the previous G. Sliepen's answer.
The experimental implementation
// recursive_count_if implementation
template<typename Pred, typename Range>
concept is_applicable_to_elements = requires(Pred& predicate, const Range &container)
{
predicate(*container.begin());
};
template<std::ranges::input_range Range, class Pred>
requires is_applicable_to_elements<Pred, Range>
constexpr auto recursive_count_if(const Range& input, const Pred& predicate)
{
return std::count_if(input.cbegin(), input.cend(), predicate);
}
template<std::ranges::input_range Range, class Pred>
requires std::ranges::input_range<std::ranges::range_value_t<Range>>
constexpr auto recursive_count_if(const Range& input, const Pred& predicate)
{
return std::transform_reduce(std::cbegin(input), std::cend(input), std::size_t{}, std::plus<std::size_t>(), [predicate](auto&& element) {
return recursive_count_if(element, predicate);
});
}
// recursive_count_if implementation (with execution policy)
template<class ExPo, std::ranges::input_range Range, class Pred>
requires (std::is_execution_policy_v<std::remove_cvref_t<ExPo>>) && (is_applicable_to_elements<Pred, Range>)
constexpr auto recursive_count_if(ExPo execution_policy, const Range& input, const Pred& predicate)
{
return std::count_if(execution_policy, input.cbegin(), input.cend(), predicate);
}
template<class ExPo, std::ranges::input_range Range, class Pred>
requires (std::is_execution_policy_v<std::remove_cvref_t<ExPo>>) && (std::ranges::input_range<std::ranges::range_value_t<Range>>)
constexpr auto recursive_count_if(ExPo execution_policy, const Range& input, const Pred& predicate)
{
return std::transform_reduce(execution_policy, std::cbegin(input), std::cend(input), std::size_t{}, std::plus<std::size_t>(), [execution_policy, predicate](auto&& element) {
return recursive_count_if(execution_policy, element, predicate);
});
}
Test cases
template<class T>
void recursive_count_if_test_template1()
{
std::cout << "recursive_count_if_test_template1" << std::endl;
// std::vector<std::vector<int>> case
std::vector<T> test_vector{ 1, 2, 3, 4, 4, 3, 7, 8, 9, 10 };
std::vector<decltype(test_vector)> test_vector2;
test_vector2.push_back(test_vector);
test_vector2.push_back(test_vector);
test_vector2.push_back(test_vector);
// use a lambda expression to count elements divisible by 3.
std::cout << "#number divisible by three in test_vector2: " << recursive_count_if(std::execution::par, test_vector2, [](T i) {return i % 3 == 0; }) << '\n';
auto test_vector3 = n_dim_container_generator<5, std::vector, decltype(test_vector)>(test_vector, 3);
// use a lambda expression to count elements divisible by 3.
std::cout << "#number divisible by three in test_vector3: " << recursive_count_if(std::execution::par, test_vector3, [](T i) {return i % 3 == 0; }) << '\n';
// std::deque<std::deque<int>> case
std::deque<T> test_deque;
test_deque.push_back(1);
test_deque.push_back(2);
test_deque.push_back(3);
std::deque<decltype(test_deque)> test_deque2;
test_deque2.push_back(test_deque);
test_deque2.push_back(test_deque);
test_deque2.push_back(test_deque);
// use a lambda expression to count elements divisible by 3.
std::cout << "#number divisible by three in test_deque2: " << recursive_count_if(std::execution::par, test_deque2, [](T i) {return i % 3 == 0; }) << '\n';
auto test_deque3 = n_dim_container_generator<5, std::deque, decltype(test_deque)>(test_deque, 3);
std::cout << "#number divisible by three in test_deque3: " << recursive_count_if(std::execution::par, test_deque3, [](T i) {return i % 3 == 0; }) << '\n';
}
int main()
{
recursive_count_if_test_template1<int>();
recursive_count_if_test_template1<short>();
recursive_count_if_test_template1<long>();
recursive_count_if_test_template1<long long int>();
recursive_count_if_test_template1<unsigned char>();
recursive_count_if_test_template1<unsigned int>();
recursive_count_if_test_template1<unsigned short int>();
recursive_count_if_test_template1<unsigned long int>();
return 0;
}
All suggestions are welcome.
The summary information:
Which question it is a follow-up to?
Avoiding requires clause if possible on a series recursive function in C++ and
What changes has been made in the code since last question?
Trying to use standard concepts wherever possible
Avoiding the unnecessary copies
Why a new review is being asked for?
Thanks so much to indi and G. Sliepen. After going through the previous answers, the improved code is as above. I am not sure if there is any other problem. If there is any possible improvement, please let me know.