# A recursive_count_if Function with Automatic Type Deducing from Lambda for Various Type Arbitrary Nested Iterable Implementation in C++

This is a follow-up question for A recursive_count_if Function For Various Type Arbitrary Nested Iterable Implementation in C++ and A recursive_count_if Function with Specified value_type for Various Type Arbitrary Nested Iterable Implementation in C++. After digging into the thing that detecting the argument type of a function, I found that it is possible to simplify the T1 parameter in last implementation with boost::callable_traits::args_t syntax in Boost.CallableTraits library. As the result, recursive_count_if template function can be used exactly as the following code.

std::vector<std::vector<std::string>> v = {{"hello"}, {"world"}};
auto size5 = [](std::string s) { return s.size() == 5; };
auto n = recursive_count_if(v, size5);


The implementation of recursive_count_if function with automatic type deducing:

#include <boost/callable_traits.hpp>
//  recursive_count_if implementation
template<class T1, class T2> requires (is_iterable<T1> && std::same_as<std::tuple<std::iter_value_t<T1>>, boost::callable_traits::args_t<T2>>)
auto recursive_count_if(const T1& input, const T2 predicate)
{
return std::count_if(input.begin(), input.end(), predicate);
}

//  transform_reduce version
template<class T1, class T2> requires (is_iterable<T1> && !std::same_as<std::tuple<std::iter_value_t<T1>>, boost::callable_traits::args_t<T2>>)
auto recursive_count_if(const T1& input, const T2 predicate)
{
return std::transform_reduce(std::begin(input), std::end(input), std::size_t{}, std::plus<std::size_t>(), [predicate](auto& element) {
return recursive_count_if(element, predicate);
});
}


The used is_iterable concept:

template<typename T>
concept is_iterable = requires(T x)
{
*std::begin(x);
std::end(x);
};


The constraints of usage

Because the type in the input lambda function plays the role of termination condition, you can not use auto keyword as generic lambdas here. If the lambda function like [](auto element) { } is passed in, compiling errors will pop up. If you want to use generic lambdas, maybe you can choose the previous version recursive_count_if function due to the termination condition is separated.

All suggestions are welcome.

The summary information:

• Which question it is a follow-up to?

A recursive_count_if Function with Specified value_type for Various Type Arbitrary Nested Iterable Implementation in C++

• What changes has been made in the code since last question?

This version of recursive_count_if template function is boost-dependent, and the type of termination condition can be deduced automatically from input lambda parameter.

• Why a new review is being asked for?

In my opinion, the requires-clause of recursive_count_if template function is a little complex and it's boost-dependent. If there is any simpler way to do this, please let me know.

• Just curious, why recursive? – bipll Nov 19 '20 at 16:43
• @bipll In order to make API more generic, the recursive technique used here to deal with arbitrary level nested iterables, including something like std::vector<std::vector<std::vector<std::string>>> or std::vector<std::vector<std::vector<std::vector<std::string>>>>... – JimmyHu Nov 19 '20 at 17:32

# Don't try to deduce the predicate's parameter type

As Quuxplusone mentioned in the comments of the predecessor question, you should not try to determine the type of the predicate function's parameter, this is bound to fail. And indeed the problem immediately starts when you look at your predicate:

auto size5 = [](std::string s) { return s.size() == 5; };


And think: hey, that's making a copy of s, I should pass that by const reference instead:

auto size5 = [](const std::string &s) { return s.size() == 5; };


Now it fails the requires clauses of your functions. You might be able to add some more tricks to cast away cv-qualifiers and references before comparing the types with std::same_as, but that does not cover all possible situations either. For example, I could write a lambda which allows any type of std::basic_string to be checked:

auto size5 = []<typename T>(std::basic_string<T> s) { return s.size() == 5; };


As I suggested in the comments, you just want to write a concept that checks if the predicate can be applied to the argument it is fed, you don't need to check the types themselves. Here is a possible implementation:

template<typename Pred, typename T>
concept is_applicable_to_elements = requires(Pred predicate, const T &container)
{
predicate(*container.begin());
};

template<class T1, class T2> requires is_applicable_to_elements<T2, T1>
auto recursive_count_if(const T1& input, const T2 predicate)
{
return std::count_if(input.begin(), input.end(), predicate);
}

template<class T1, class T2>
auto recursive_count_if(const T1& input, const T2 predicate)
{
return std::transform_reduce(std::begin(input), std::end(input), std::size_t{}, std::plus<std::size_t>(), [predicate](auto& element) {
return recursive_count_if(element, predicate);
});
}


Note that since all the templates used is_iterable(), you can remove that requirement, although if you keep it you get nicer error messages if you accidentily try to apply recursive_count_if() on something that does not support iterating over.