2
\$\begingroup\$

This is a follow-up question for A recursive_transform for std::vector with various return type and A recursive_transform Template Function Implementation with std::invocable concept in C++. Besides the recursive version std::ranges::transform, I am trying to implement a recursive version std::ranges::copy_if.

The experimental implementation

The experimental implementation is as below.

// recursive_copy_if function 
template <std::ranges::input_range Range, std::invocable<std::ranges::range_value_t<Range>> UnaryPredicate>
constexpr auto recursive_copy_if(const Range& input, const UnaryPredicate& unary_predicate)
{
 Range output{};
 std::ranges::copy_if(std::ranges::cbegin(input), std::ranges::cend(input),
 std::inserter(output, std::ranges::end(output)),
 unary_predicate);
 return output;
}
template <
 std::ranges::input_range Range,
 class UnaryPredicate>
requires (!std::invocable<UnaryPredicate, std::ranges::range_value_t<Range>>)
constexpr auto recursive_copy_if(const Range& input, const UnaryPredicate& unary_predicate)
{
 Range output{};
 
 std::ranges::transform(
 std::ranges::cbegin(input),
 std::ranges::cend(input),
 std::inserter(output, std::ranges::end(output)),
 [&unary_predicate](auto&& element) { return recursive_copy_if(element, unary_predicate); }
 );
 return output;
}

Test cases

// std::vector<int>
std::vector<int> test_vector = {
 1, 2, 3, 4, 5, 6
};
recursive_print(recursive_copy_if(test_vector, [](int x) { return (x % 2) == 0; }));
// std::vector<std::vector<int>>
std::vector<decltype(test_vector)> test_vector2 = {
 test_vector, test_vector, test_vector
};
recursive_print(recursive_copy_if(test_vector2, [](int x) { return (x % 2) == 0; }));
// std::vector<std::string>
recursive_print(
 recursive_copy_if(
 recursive_transform(test_vector, [](int x) { return std::to_string(x); }),
 [](std::string x) { return (x == "1"); }
 )
);
// std::vector<std::vector<std::string>>
recursive_print(
 recursive_copy_if(
 recursive_transform(test_vector2, [](int x) { return std::to_string(x); }),
 [](std::string x) { return (x == "1"); }
 )
);
// std::deque<int>
std::deque<int> test_deque;
test_deque.push_back(1);
test_deque.push_back(2);
test_deque.push_back(3);
test_deque.push_back(4);
test_deque.push_back(5);
test_deque.push_back(6);
recursive_print(recursive_copy_if(test_deque, [](int x) { return (x % 2) == 0; }));
// std::deque<std::deque<int>>
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);
recursive_print(recursive_copy_if(test_deque2, [](int x) { return (x % 2) == 0; }));
// std::list<int>
std::list<int> test_list = { 1, 2, 3, 4, 5, 6 };
recursive_print(recursive_copy_if(test_list, [](int x) { return (x % 2) == 0; }));
// std::list<std::list<int>>
std::list<std::list<int>> test_list2 = { test_list, test_list, test_list, test_list };
recursive_print(recursive_copy_if(test_list2, [](int x) { return (x % 2) == 0; }));
Full Testing Code

The full testing code:

#include <algorithm>
#include <array>
#include <cassert>
#include <chrono>
#include <complex>
#include <concepts>
#include <deque>
#include <execution>
#include <exception>
#include <functional>
#include <iostream>
#include <iterator>
#include <list>
#include <map>
#include <mutex>
#include <numeric>
#include <optional>
#include <ranges>
#include <stdexcept>
#include <string>
#include <tuple>
#include <type_traits>
#include <utility>
#include <variant>
#include <vector>
// recursive_copy_if function 
template <std::ranges::input_range Range, std::invocable<std::ranges::range_value_t<Range>> UnaryPredicate>
constexpr auto recursive_copy_if(const Range& input, const UnaryPredicate& unary_predicate)
{
 Range output{};
 std::ranges::copy_if(std::ranges::cbegin(input), std::ranges::cend(input),
 std::inserter(output, std::ranges::end(output)),
 unary_predicate);
 return output;
}
template <
 std::ranges::input_range Range,
 class UnaryPredicate>
requires (!std::invocable<UnaryPredicate, std::ranges::range_value_t<Range>>)
constexpr auto recursive_copy_if(const Range& input, const UnaryPredicate& unary_predicate)
{
 Range output{};
 
 std::ranges::transform(
 std::ranges::cbegin(input),
 std::ranges::cend(input),
 std::inserter(output, std::ranges::end(output)),
 [&unary_predicate](auto&& element) { return recursive_copy_if(element, unary_predicate); }
 );
 return output;
}
// recursive_print implementation
// https://codereview.stackexchange.com/q/251208/231235
template<std::ranges::input_range Range>
constexpr auto recursive_print(const Range& input, const int level = 0)
{
 auto output = input;
 std::cout << std::string(level, ' ') << "Level " << level << ":" << std::endl;
 std::transform(std::ranges::cbegin(input), std::ranges::cend(input), std::ranges::begin(output),
 [level](auto&& x)
 {
 std::cout << std::string(level, ' ') << x << std::endl;
 return x;
 }
 );
 return output;
}
template<std::ranges::input_range Range> requires (std::ranges::input_range<std::ranges::range_value_t<Range>>)
constexpr auto recursive_print(const Range& input, const int level = 0)
{
 auto output = input;
 std::cout << std::string(level, ' ') << "Level " << level << ":" << std::endl;
 std::transform(std::ranges::cbegin(input), std::ranges::cend(input), std::ranges::begin(output),
 [level](auto&& element)
 {
 return recursive_print(element, level + 1);
 }
 );
 return output;
}
// recursive_invoke_result_t implementation
template<typename, typename>
struct recursive_invoke_result { };
template<typename T, std::invocable<T> F>
struct recursive_invoke_result<F, T> { using type = std::invoke_result_t<F, T>; };
template<typename F, template<typename...> typename Container, typename... Ts>
requires (
 !std::invocable<F, Container<Ts...>> &&
 std::ranges::input_range<Container<Ts...>> &&
 requires { typename recursive_invoke_result<F, std::ranges::range_value_t<Container<Ts...>>>::type; })
struct recursive_invoke_result<F, Container<Ts...>>
{
 using type = Container<typename recursive_invoke_result<F, std::ranges::range_value_t<Container<Ts...>>>::type>;
};
template<typename F, typename T>
using recursive_invoke_result_t = typename recursive_invoke_result<F, T>::type;
// recursive_transform implementation
template <class T, std::invocable<T> F>
constexpr auto recursive_transform(const T& input, const F& f)
{
 return f(input);
}
template <
 std::ranges::input_range Range,
 class F>
requires (!std::invocable<F, Range>)
constexpr auto recursive_transform(const Range& input, const F& f)
{
 recursive_invoke_result_t<F, Range> output{};
 
 std::ranges::transform(
 std::ranges::cbegin(input),
 std::ranges::cend(input),
 std::inserter(output, std::ranges::end(output)),
 [&f](auto&& element) { return recursive_transform(element, f); }
 );
 return output;
}
int main()
{
 // std::vector<int>
 std::vector<int> test_vector = {
 1, 2, 3, 4, 5, 6
 };
 recursive_print(recursive_copy_if(test_vector, [](int x) { return (x % 2) == 0; }));
 // std::vector<std::vector<int>>
 std::vector<decltype(test_vector)> test_vector2 = {
 test_vector, test_vector, test_vector
 };
 recursive_print(recursive_copy_if(test_vector2, [](int x) { return (x % 2) == 0; }));
 
 // std::vector<std::string>
 recursive_print(
 recursive_copy_if(
 recursive_transform(test_vector, [](int x) { return std::to_string(x); }),
 [](std::string x) { return (x == "1"); }
 )
 );
 // std::vector<std::vector<std::string>>
 recursive_print(
 recursive_copy_if(
 recursive_transform(test_vector2, [](int x) { return std::to_string(x); }),
 [](std::string x) { return (x == "1"); }
 )
 );
 // std::deque<int>
 std::deque<int> test_deque;
 test_deque.push_back(1);
 test_deque.push_back(2);
 test_deque.push_back(3);
 test_deque.push_back(4);
 test_deque.push_back(5);
 test_deque.push_back(6);
 recursive_print(recursive_copy_if(test_deque, [](int x) { return (x % 2) == 0; }));
 // std::deque<std::deque<int>>
 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);
 recursive_print(recursive_copy_if(test_deque2, [](int x) { return (x % 2) == 0; }));
 // std::list<int>
 std::list<int> test_list = { 1, 2, 3, 4, 5, 6 };
 recursive_print(recursive_copy_if(test_list, [](int x) { return (x % 2) == 0; }));
 // std::list<std::list<int>>
 std::list<std::list<int>> test_list2 = { test_list, test_list, test_list, test_list };
 recursive_print(recursive_copy_if(test_list2, [](int x) { return (x % 2) == 0; }));
 return 0;
}

A Godbolt link is here.

All suggestions are welcome.

The summary information:

asked Jan 1, 2021 at 15:52
\$\endgroup\$

1 Answer 1

1
\$\begingroup\$

You don't need this line:

requires (!std::invocable<UnaryPredicate, std::ranges::range_value_t<Range>>)

And your test cases miss a std::vector<std::string> as input, but your recursive_copy_if() works fine on those as well.

answered Jan 1, 2021 at 18:01
\$\endgroup\$

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.