1
\$\begingroup\$

This is a follow-up question for A recursive_replace_if Template Function Implementation in C++, A recursive_copy_if Template Function Implementation in C++, A recursive_count_if Function with Unwrap Level for Various Type Arbitrary Nested Iterable Implementation in C++ and A recursive_count_if Function For Various Type Arbitrary Nested Iterable Implementation in C++. After implemented recursive_replace_if and recursive_copy_if, I am trying to generalize the operation of applying a function on elements in nested container. The unwrap level parameter is used here instead of std::invocable as the termination condition of the recursion process.

The experimental implementation

The experimental implementation is as below.

//  recursive_function_applier implementation
template<std::size_t unwrap_level, class F, std::ranges::range Range, class... Args>
constexpr auto recursive_function_applier(const F& function, const Range& input, Args... args)
{
    if constexpr (unwrap_level >= 1)
    {
        Range output{};
        std::ranges::transform(
            std::ranges::cbegin(input),
            std::ranges::cend(input),
            std::inserter(output, std::ranges::end(output)),
            [&function, &args...](auto&& element) { return recursive_function_applier<unwrap_level - 1>(function, element, args...); }
        );
        return output;
    }
    else
    {
        Range output{};
        function(
            std::ranges::cbegin(input),
            std::ranges::cend(input),
            std::inserter(output, std::ranges::end(output)),
            args...);
        return output;
    }
}

Test cases

  • Test cases with using std::ranges::copy function

    //  std::copy test cases
    //  std::vector<int>
    std::vector<int> test_vector{ 5, 7, 4, 2, 8, 6, 1, 9, 0, 3 };
    recursive_print(recursive_function_applier<0>(std::ranges::copy, test_vector));
    
    //  std::vector<std::vector<int>>
    std::vector<decltype(test_vector)> test_vector2{ test_vector , test_vector , test_vector };
    recursive_print(recursive_function_applier<1>(std::ranges::copy, test_vector2));
    
    //  std::vector<std::string>
    std::vector<std::string> test_string_vector{ "0", "1", "2", "3", "4", "5", "6", "7", "8", "9", "10", "11", "12", "13", "14", "15", "16", "17", "18", "19", "20" };
    recursive_print(
        recursive_function_applier<0>(
            std::ranges::copy, test_string_vector
        )
    );
    
    //  std::vector<std::vector<std::string>>
    std::vector<decltype(test_string_vector)> test_string_vector2{ test_string_vector , test_string_vector , test_string_vector };
    recursive_print(
        recursive_function_applier<1>(
            std::ranges::copy, test_string_vector2
        )
    );
    
    //  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_function_applier<0>(std::ranges::copy, test_deque));
    
    //  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_function_applier<1>(std::ranges::copy, test_deque2));
    
    //  std::list<int>
    std::list<int> test_list = { 1, 2, 3, 4, 5, 6 };
    recursive_print(recursive_function_applier<0>(std::ranges::copy, test_list));
    
    
    //  std::list<std::list<int>>
    std::list<std::list<int>> test_list2 = { test_list, test_list, test_list, test_list };
    recursive_print(recursive_function_applier<1>(std::ranges::copy, test_list2));
    
    recursive_print(
        recursive_function_applier<10>(
            std::ranges::copy,
            n_dim_container_generator<10, std::list>(test_list, 3)
            )
    );
    
  • Test cases with using std::ranges::replace_copy_if function

    //  std::ranges::replace_copy_if test cases
    //  std::vector<int>
    std::vector<int> test_vector{ 5, 7, 4, 2, 8, 6, 1, 9, 0, 3 };
    recursive_print(recursive_function_applier<0>(std::ranges::replace_copy_if, test_vector, std::bind(std::less<int>(), std::placeholders::_1, 5), 55));
    
    //  std::vector<std::vector<int>>
    std::vector<decltype(test_vector)> test_vector2{ test_vector , test_vector , test_vector };
    recursive_print(recursive_function_applier<1>(std::ranges::replace_copy_if, test_vector2, std::bind(std::less<int>(), std::placeholders::_1, 5), 55));
    
    //  std::vector<std::string>
    std::vector<std::string> test_string_vector{ "0", "1", "2", "3", "4", "5", "6", "7", "8", "9", "10", "11", "12", "13", "14", "15", "16", "17", "18", "19", "20" };
    recursive_print(
        recursive_function_applier<0>(
            std::ranges::replace_copy_if, test_string_vector, [](std::string x) { return (x == "1"); }, "11"
        )
    );
    
    //  std::vector<std::vector<std::string>>
    std::vector<decltype(test_string_vector)> test_string_vector2{ test_string_vector , test_string_vector , test_string_vector };
    recursive_print(
        recursive_function_applier<1>(
            std::ranges::replace_copy_if, test_string_vector2, [](std::string x) { return (x == "1"); }, "11"
        )
    );
    
    //  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_function_applier<0>(std::ranges::replace_copy_if, test_deque, [](int x) { return (x % 2) == 0; }, 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_function_applier<1>(std::ranges::replace_copy_if, test_deque2, [](int x) { return (x % 2) == 0; }, 0));
    
    //  std::list<int>
    std::list<int> test_list = { 1, 2, 3, 4, 5, 6 };
    recursive_print(recursive_function_applier<0>(std::ranges::replace_copy_if, test_list, [](int x) { return (x % 2) == 0; }, 0));
    
    
    //  std::list<std::list<int>>
    std::list<std::list<int>> test_list2 = { test_list, test_list, test_list, test_list };
    recursive_print(recursive_function_applier<1>(std::ranges::replace_copy_if, test_list2, [](int x) { return (x % 2) == 0; }, 0));
    
    recursive_print(
        recursive_function_applier<10>(
            std::ranges::replace_copy_if,
            n_dim_container_generator<10, std::list>(test_list, 3),
            [](int x) { return (x % 2) == 0; },
            0
            )
    );
    
  • Test cases with using std::ranges::remove_copy function

    //  std::remove_copy test cases
    //  std::vector<int>
    std::vector<int> test_vector{ 5, 7, 4, 2, 8, 6, 1, 9, 0, 3 };
    recursive_print(recursive_function_applier<0>(std::ranges::remove_copy, test_vector, 5));
    
    //  std::vector<std::vector<int>>
    std::vector<decltype(test_vector)> test_vector2{ test_vector , test_vector , test_vector };
    recursive_print(recursive_function_applier<1>(std::ranges::remove_copy, test_vector2, 5));
    
    //  std::vector<std::string>
    std::vector<std::string> test_string_vector{ "0", "1", "2", "3", "4", "5", "6", "7", "8", "9", "10", "11", "12", "13", "14", "15", "16", "17", "18", "19", "20" };
    recursive_print(
        recursive_function_applier<0>(
            std::ranges::remove_copy, test_string_vector, "0"
        )
    );
    
    //  std::vector<std::vector<std::string>>
    std::vector<decltype(test_string_vector)> test_string_vector2{ test_string_vector , test_string_vector , test_string_vector };
    recursive_print(
        recursive_function_applier<1>(
            std::ranges::remove_copy, test_string_vector2, "0"
        )
    );
    
    //  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_function_applier<0>(std::ranges::remove_copy, test_deque, 1));
    
    //  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_function_applier<1>(std::ranges::remove_copy, test_deque2, 1));
    
    //  std::list<int>
    std::list<int> test_list = { 1, 2, 3, 4, 5, 6 };
    recursive_print(recursive_function_applier<0>(std::ranges::remove_copy, test_list, 1));
    
    
    //  std::list<std::list<int>>
    std::list<std::list<int>> test_list2 = { test_list, test_list, test_list, test_list };
    recursive_print(recursive_function_applier<1>(std::ranges::remove_copy, test_list2, 1));
    
    recursive_print(
        recursive_function_applier<10>(
            std::ranges::remove_copy,
            n_dim_container_generator<10, std::list>(test_list, 3),
            1
            )
    );
    
  • Test cases with using std::ranges::remove_copy_if function

    //  std::remove_copy_if test cases
    //  std::vector<int>
    std::vector<int> test_vector{ 5, 7, 4, 2, 8, 6, 1, 9, 0, 3 };
    recursive_print(recursive_function_applier<0>(std::ranges::remove_copy_if, test_vector, std::bind(std::less<int>(), std::placeholders::_1, 5)));
    
    //  std::vector<std::vector<int>>
    std::vector<decltype(test_vector)> test_vector2{ test_vector , test_vector , test_vector };
    recursive_print(recursive_function_applier<1>(std::ranges::remove_copy_if, test_vector2, std::bind(std::less<int>(), std::placeholders::_1, 5)));
    
    //  std::vector<std::string>
    std::vector<std::string> test_string_vector{ "0", "1", "2", "3", "4", "5", "6", "7", "8", "9", "10", "11", "12", "13", "14", "15", "16", "17", "18", "19", "20" };
    recursive_print(
      recursive_function_applier<0>(
          std::ranges::remove_copy_if, test_string_vector, [](std::string x) { return (x == "0"); }
      )
    );
    
    //  std::vector<std::vector<std::string>>
    std::vector<decltype(test_string_vector)> test_string_vector2{ test_string_vector , test_string_vector , test_string_vector };
    recursive_print(
      recursive_function_applier<1>(
          std::ranges::remove_copy_if, test_string_vector2, [](std::string x) { return (x == "0"); }
      )
    );
    
    //  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_function_applier<0>(std::ranges::remove_copy_if, test_deque, std::bind(std::less<int>(), std::placeholders::_1, 5)));
    
    //  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_function_applier<1>(std::ranges::remove_copy_if, test_deque2, std::bind(std::less<int>(), std::placeholders::_1, 5)));
    
    //  std::list<int>
    std::list<int> test_list = { 1, 2, 3, 4, 5, 6 };
    recursive_print(recursive_function_applier<0>(std::ranges::remove_copy_if, test_list, std::bind(std::less<int>(), std::placeholders::_1, 5)));
    
    
    //  std::list<std::list<int>>
    std::list<std::list<int>> test_list2 = { test_list, test_list, test_list, test_list };
    recursive_print(recursive_function_applier<1>(std::ranges::remove_copy_if, test_list2, std::bind(std::less<int>(), std::placeholders::_1, 5)));
    
    recursive_print(
      recursive_function_applier<5>(
          std::ranges::remove_copy_if,
          n_dim_container_generator<5, std::list>(test_list, 3),
          std::bind(std::less<int>(), std::placeholders::_1, 5)
          )
    );
    

Full Testing Code

The full testing code:

//  A Function Applier for Applying Various Algorithms on Nested Container Things in C++

#include <algorithm>
#include <array>
#include <cassert>
#include <chrono>
#include <complex>
#include <concepts>
#include <deque>
#include <exception>
#include <execution>
#include <functional>
#include <iostream>
#include <iterator>
#include <list>
#include <map>
#include <numeric>
#include <optional>
#include <ranges>
#include <stdexcept>
#include <string>
#include <type_traits>
#include <utility>
#include <variant>
#include <vector>

template<typename T>
concept is_inserterable = requires(T x)
{
    std::inserter(x, std::ranges::end(x));
};

#ifdef USE_BOOST_MULTIDIMENSIONAL_ARRAY
template<typename T>
concept is_multi_array = requires(T x)
{
    x.num_dimensions();
    x.shape();
    boost::multi_array(x);
};
#endif

//  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>
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_count implementation
template<std::ranges::input_range Range, typename T>
constexpr auto recursive_count(const Range& input, const T& target)
{
    return std::count(std::ranges::cbegin(input), std::ranges::cend(input), target);
}

//  transform_reduce version
template<std::ranges::input_range Range, typename T>
requires std::ranges::input_range<std::ranges::range_value_t<Range>>
constexpr auto recursive_count(const Range& input, const T& target)
{
    return std::transform_reduce(std::ranges::cbegin(input), std::ranges::cend(input), std::size_t{}, std::plus<std::size_t>(), [target](auto&& element) {
        return recursive_count(element, target);
        });
}

//  recursive_count implementation (with execution policy)
template<class ExPo, std::ranges::input_range Range, typename T>
requires (std::is_execution_policy_v<std::remove_cvref_t<ExPo>>)
constexpr auto recursive_count(ExPo execution_policy, const Range& input, const T& target)
{
    return std::count(execution_policy, std::ranges::cbegin(input), std::ranges::cend(input), target);
}

template<class ExPo, std::ranges::input_range Range, typename T>
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(ExPo execution_policy, const Range& input, const T& target)
{
    return std::transform_reduce(execution_policy, std::ranges::cbegin(input), std::ranges::cend(input), std::size_t{}, std::plus<std::size_t>(), [execution_policy, target](auto&& element) {
        return recursive_count(execution_policy, element, target);
        });
}

//  recursive_count_if implementation
template<class T, std::invocable<T> Pred>
constexpr std::size_t recursive_count_if(const T& input, const Pred& predicate)
{
    return predicate(input) ? 1 : 0;
}

template<std::ranges::input_range Range, class Pred>
requires (!std::invocable<Pred, Range>)
constexpr auto recursive_count_if(const Range& input, const Pred& predicate)
{
    return std::transform_reduce(std::ranges::cbegin(input), std::ranges::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, class T, std::invocable<T> Pred>
requires (std::is_execution_policy_v<std::remove_cvref_t<ExPo>>)
constexpr std::size_t recursive_count_if(ExPo execution_policy, const T& input, const Pred& predicate)
{
    return predicate(input) ? 1 : 0;
}

template<class ExPo, std::ranges::input_range Range, class Pred>
requires ((std::is_execution_policy_v<std::remove_cvref_t<ExPo>>) && (!std::invocable<Pred, Range>))
constexpr auto recursive_count_if(ExPo execution_policy, const Range& input, const Pred& predicate)
{
    return std::transform_reduce(execution_policy, std::ranges::cbegin(input), std::ranges::cend(input), std::size_t{}, std::plus<std::size_t>(), [predicate](auto&& element) {
        return recursive_count_if(element, predicate);
    });
}

//  recursive_count_if implementation (the version with unwrap_level)
template<std::size_t unwrap_level, std::ranges::range T, class Pred>
auto recursive_count_if(const T& input, const Pred& predicate)
{
    if constexpr (unwrap_level > 1)
    {
        return std::transform_reduce(std::ranges::cbegin(input), std::ranges::cend(input), std::size_t{}, std::plus<std::size_t>(), [predicate](auto&& element) {
            return recursive_count_if<unwrap_level - 1>(element, predicate);
            });
    }
    else
    {
        return std::count_if(std::ranges::cbegin(input), std::ranges::cend(input), predicate);
    }
}

//  recursive_function_applier implementation
template<std::size_t unwrap_level, class F, std::ranges::range Range, class... Args>
constexpr auto recursive_function_applier(const F& function, const Range& input, Args... args)
{
    if constexpr (unwrap_level >= 1)
    {
        Range output{};
        std::ranges::transform(
            std::ranges::cbegin(input),
            std::ranges::cend(input),
            std::inserter(output, std::ranges::end(output)),
            [&function, &args...](auto&& element) { return recursive_function_applier<unwrap_level - 1>(function, element, args...); }
        );
        return output;
    }
    else
    {
        Range output{};
        function(
            std::ranges::cbegin(input),
            std::ranges::cend(input),
            std::inserter(output, std::ranges::end(output)),
            args...);
        return output;
    }
}

//  recursive_print implementation
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::ranges::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::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;
}

//  recursive_replace_copy_if implementation
template<std::ranges::range Range, std::invocable<std::ranges::range_value_t<Range>> UnaryPredicate, class T>
constexpr auto recursive_replace_copy_if(const Range& input, const UnaryPredicate& unary_predicate, const T& new_value)
{
    Range output{};
    std::ranges::replace_copy_if(
        std::ranges::cbegin(input),
        std::ranges::cend(input),
        std::inserter(output, std::ranges::end(output)),
        unary_predicate,
        new_value);
    return output;
}

template<std::ranges::input_range Range, class UnaryPredicate, class T>
requires (!std::invocable<UnaryPredicate, std::ranges::range_value_t<Range>>)
constexpr auto recursive_replace_copy_if(const Range& input, const UnaryPredicate& unary_predicate, const T& new_value)
{
    Range output{};

    std::ranges::transform(
        std::ranges::cbegin(input),
        std::ranges::cend(input),
        std::inserter(output, std::ranges::end(output)),
        [&unary_predicate, &new_value](auto&& element) { return recursive_replace_copy_if(element, unary_predicate, new_value); }
    );
    return output;
}

//  recursive_size implementation
template<class T> requires (!std::ranges::range<T>)
constexpr auto recursive_size(const T& input)
{
    return 1;
}

template<std::ranges::range Range> requires (!(std::ranges::input_range<std::ranges::range_value_t<Range>>))
constexpr auto recursive_size(const Range& input)
{
    return std::ranges::size(input);
}

template<std::ranges::range Range> requires (std::ranges::input_range<std::ranges::range_value_t<Range>>)
constexpr auto recursive_size(const Range& input)
{
    return std::transform_reduce(std::ranges::begin(input), std::end(input), std::size_t{}, std::plus<std::size_t>(), [](auto& element) {
        return recursive_size(element);
        });
}

//  recursive_transform implementation
//  recursive_invoke_result_t implementation
//  from https://stackoverflow.com/a/65504127/6667035
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;

template <std::ranges::range Range>
constexpr auto get_output_iterator(Range& output)
{
    return std::inserter(output, std::ranges::end(output));
}

template <class T, std::invocable<T> F>
constexpr auto recursive_transform(const T& input, const F& f)
{
    return std::invoke(f, input);        //  use std::invoke() instead, https://codereview.stackexchange.com/a/283364/231235
}

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;
}

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::begin(output), std::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, template<class...> class Container = std::vector, class T>
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, Container, T>(input, times));
    }
}

void copy_test();
void replace_copy_if_test();
void remove_copy_test();
void remove_copy_if_test();

int main()
{
    copy_test();
    replace_copy_if_test();
    remove_copy_test();
    remove_copy_if_test();
    return 0;
}

void copy_test()
{
    //  std::copy test cases
    //  std::vector<int>
    std::vector<int> test_vector{ 5, 7, 4, 2, 8, 6, 1, 9, 0, 3 };
    recursive_print(recursive_function_applier<0>(std::ranges::copy, test_vector));

    //  std::vector<std::vector<int>>
    std::vector<decltype(test_vector)> test_vector2{ test_vector , test_vector , test_vector };
    recursive_print(recursive_function_applier<1>(std::ranges::copy, test_vector2));

    //  std::vector<std::string>
    std::vector<std::string> test_string_vector{ "0", "1", "2", "3", "4", "5", "6", "7", "8", "9", "10", "11", "12", "13", "14", "15", "16", "17", "18", "19", "20" };
    recursive_print(
        recursive_function_applier<0>(
            std::ranges::copy, test_string_vector
        )
    );

    //  std::vector<std::vector<std::string>>
    std::vector<decltype(test_string_vector)> test_string_vector2{ test_string_vector , test_string_vector , test_string_vector };
    recursive_print(
        recursive_function_applier<1>(
            std::ranges::copy, test_string_vector2
        )
    );

    //  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_function_applier<0>(std::ranges::copy, test_deque));

    //  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_function_applier<1>(std::ranges::copy, test_deque2));

    //  std::list<int>
    std::list<int> test_list = { 1, 2, 3, 4, 5, 6 };
    recursive_print(recursive_function_applier<0>(std::ranges::copy, test_list));


    //  std::list<std::list<int>>
    std::list<std::list<int>> test_list2 = { test_list, test_list, test_list, test_list };
    recursive_print(recursive_function_applier<1>(std::ranges::copy, test_list2));

    recursive_print(
        recursive_function_applier<5>(
            std::ranges::copy,
            n_dim_container_generator<5, std::list>(test_list, 3)
            )
    );
    return;
}

void replace_copy_if_test()
{
    //  std::ranges::replace_copy_if test cases
    //  std::vector<int>
    std::vector<int> test_vector{ 5, 7, 4, 2, 8, 6, 1, 9, 0, 3 };
    recursive_print(recursive_function_applier<0>(std::ranges::replace_copy_if, test_vector, std::bind(std::less<int>(), std::placeholders::_1, 5), 55));

    //  std::vector<std::vector<int>>
    std::vector<decltype(test_vector)> test_vector2{ test_vector , test_vector , test_vector };
    recursive_print(recursive_function_applier<1>(std::ranges::replace_copy_if, test_vector2, std::bind(std::less<int>(), std::placeholders::_1, 5), 55));

    //  std::vector<std::string>
    std::vector<std::string> test_string_vector{ "0", "1", "2", "3", "4", "5", "6", "7", "8", "9", "10", "11", "12", "13", "14", "15", "16", "17", "18", "19", "20" };
    recursive_print(
        recursive_function_applier<0>(
            std::ranges::replace_copy_if, test_string_vector, [](std::string x) { return (x == "1"); }, "11"
        )
    );

    //  std::vector<std::vector<std::string>>
    std::vector<decltype(test_string_vector)> test_string_vector2{ test_string_vector , test_string_vector , test_string_vector };
    recursive_print(
        recursive_function_applier<1>(
            std::ranges::replace_copy_if, test_string_vector2, [](std::string x) { return (x == "1"); }, "11"
        )
    );

    //  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_function_applier<0>(std::ranges::replace_copy_if, test_deque, [](int x) { return (x % 2) == 0; }, 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_function_applier<1>(std::ranges::replace_copy_if, test_deque2, [](int x) { return (x % 2) == 0; }, 0));

    //  std::list<int>
    std::list<int> test_list = { 1, 2, 3, 4, 5, 6 };
    recursive_print(recursive_function_applier<0>(std::ranges::replace_copy_if, test_list, [](int x) { return (x % 2) == 0; }, 0));


    //  std::list<std::list<int>>
    std::list<std::list<int>> test_list2 = { test_list, test_list, test_list, test_list };
    recursive_print(recursive_function_applier<1>(std::ranges::replace_copy_if, test_list2, [](int x) { return (x % 2) == 0; }, 0));

    recursive_print(
        recursive_function_applier<5>(
            std::ranges::replace_copy_if,
            n_dim_container_generator<5, std::list>(test_list, 3),
            [](int x) { return (x % 2) == 0; },
            0
            )
    );
    return;
}

void remove_copy_test()
{
    //  std::remove_copy test cases
    //  std::vector<int>
    std::vector<int> test_vector{ 5, 7, 4, 2, 8, 6, 1, 9, 0, 3 };
    recursive_print(recursive_function_applier<0>(std::ranges::remove_copy, test_vector, 5));

    //  std::vector<std::vector<int>>
    std::vector<decltype(test_vector)> test_vector2{ test_vector , test_vector , test_vector };
    recursive_print(recursive_function_applier<1>(std::ranges::remove_copy, test_vector2, 5));

    //  std::vector<std::string>
    std::vector<std::string> test_string_vector{ "0", "1", "2", "3", "4", "5", "6", "7", "8", "9", "10", "11", "12", "13", "14", "15", "16", "17", "18", "19", "20" };
    recursive_print(
        recursive_function_applier<0>(
            std::ranges::remove_copy, test_string_vector, "0"
        )
    );

    //  std::vector<std::vector<std::string>>
    std::vector<decltype(test_string_vector)> test_string_vector2{ test_string_vector , test_string_vector , test_string_vector };
    recursive_print(
        recursive_function_applier<1>(
            std::ranges::remove_copy, test_string_vector2, "0"
        )
    );

    //  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_function_applier<0>(std::ranges::remove_copy, test_deque, 1));

    //  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_function_applier<1>(std::ranges::remove_copy, test_deque2, 1));

    //  std::list<int>
    std::list<int> test_list = { 1, 2, 3, 4, 5, 6 };
    recursive_print(recursive_function_applier<0>(std::ranges::remove_copy, test_list, 1));


    //  std::list<std::list<int>>
    std::list<std::list<int>> test_list2 = { test_list, test_list, test_list, test_list };
    recursive_print(recursive_function_applier<1>(std::ranges::remove_copy, test_list2, 1));

    recursive_print(
        recursive_function_applier<5>(
            std::ranges::remove_copy,
            n_dim_container_generator<5, std::list>(test_list, 3),
            1
            )
    );
    return;
}

void remove_copy_if_test()
{
    //  std::remove_copy_if test cases
    //  std::vector<int>
    std::vector<int> test_vector{ 5, 7, 4, 2, 8, 6, 1, 9, 0, 3 };
    recursive_print(recursive_function_applier<0>(std::ranges::remove_copy_if, test_vector, std::bind(std::less<int>(), std::placeholders::_1, 5)));

    //  std::vector<std::vector<int>>
    std::vector<decltype(test_vector)> test_vector2{ test_vector , test_vector , test_vector };
    recursive_print(recursive_function_applier<1>(std::ranges::remove_copy_if, test_vector2, std::bind(std::less<int>(), std::placeholders::_1, 5)));

    //  std::vector<std::string>
    std::vector<std::string> test_string_vector{ "0", "1", "2", "3", "4", "5", "6", "7", "8", "9", "10", "11", "12", "13", "14", "15", "16", "17", "18", "19", "20" };
    recursive_print(
        recursive_function_applier<0>(
            std::ranges::remove_copy_if, test_string_vector, [](std::string x) { return (x == "0"); }
        )
    );

    //  std::vector<std::vector<std::string>>
    std::vector<decltype(test_string_vector)> test_string_vector2{ test_string_vector , test_string_vector , test_string_vector };
    recursive_print(
        recursive_function_applier<1>(
            std::ranges::remove_copy_if, test_string_vector2, [](std::string x) { return (x == "0"); }
        )
    );

    //  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_function_applier<0>(std::ranges::remove_copy_if, test_deque, std::bind(std::less<int>(), std::placeholders::_1, 5)));

    //  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_function_applier<1>(std::ranges::remove_copy_if, test_deque2, std::bind(std::less<int>(), std::placeholders::_1, 5)));

    //  std::list<int>
    std::list<int> test_list = { 1, 2, 3, 4, 5, 6 };
    recursive_print(recursive_function_applier<0>(std::ranges::remove_copy_if, test_list, std::bind(std::less<int>(), std::placeholders::_1, 5)));


    //  std::list<std::list<int>>
    std::list<std::list<int>> test_list2 = { test_list, test_list, test_list, test_list };
    recursive_print(recursive_function_applier<1>(std::ranges::remove_copy_if, test_list2, std::bind(std::less<int>(), std::placeholders::_1, 5)));

    recursive_print(
        recursive_function_applier<5>(
            std::ranges::remove_copy_if,
            n_dim_container_generator<5, std::list>(test_list, 3),
            std::bind(std::less<int>(), std::placeholders::_1, 5)
            )
    );
    return;
}

A Godbolt link is here.

All suggestions are welcome.

The summary information:

\$\endgroup\$

1 Answer 1

1
\$\begingroup\$

What does it do?

I am trying to generalize the operation of applying a function on elements in nested container.

If you say that, I would think that your recursive_transform() would already cover it. It seems that this "function applier" is actually more a "container algorithm applier"; it recurses a nested container to a given level, expects that every element at that point is also a container, and then calls the given function, expecting it to have a similar interface as STL algorithms such as std::copy(): they get two input iterators and an output iterator as arguments.

An alternative approach

I think the only reason for this function is to deal with the fact that STL algorithms never return a container. You could say that they are not working in a functional programming style. If they did, then you could just easily compose your recursive_transform() with a copy() function that does return a container. So maybe you should make those instead? Consider:

struct SameAsInput;

template<typename Output = SameAsInput, typename Input>
auto return_copy(const Input& input) {
     std::conditional_t<
         std::is_same_v<Output, SameAsInput>,
         Input,
         Output
     > output;
     std::ranges::copy(input, std::inserter(output, std::ranges::end(output)));
     return output;
}

Then instead of:

recursive_function_applier<0>(std::ranges::copy, test_vector);

You could write:

recursive_transform<0>(test_vector, return_copy);

You can make return_copy_n(), return_remove_copy(), return_remove_copy_if(), and so on.

Restricting the container type

You are using the concept std::ranges::range to restrict the type of input. However, you want to create a container that has the same type of input, and not every range is a container. For example, views are ranges too. Unfortunately, there is no std::container concept, but you can make one yourself.

\$\endgroup\$

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge that you have read and understand our privacy policy and code of conduct.

Not the answer you're looking for? Browse other questions tagged or ask your own question.