Шаблонная функция recursive_transform с уровнем распаковки для произвольной вложенной итерируемой реализации произвольного типа в C ++

Это дополнительный вопрос для реализации шаблона функции recursive_transform с концепцией std :: invocable в C ++ и реализации функции шаблона recursive_transform с recursive_invoke_result_t и std :: range :: transform в C ++. Помимо версии, использующей std::invocable, Я пытаюсь реализовать другой тип recursive_transform функция с параметром уровня разворачивания, чтобы использование, подобное recursive_transform<1>(Ranges, Lambda) доступен.

Экспериментальная реализация

Экспериментальная реализация recursive_transform функция с параметром уровня разворачивания выглядит следующим образом.

//  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 (the version with unwrap_level)
template<std::size_t unwrap_level = 1, class T, class F>
constexpr auto recursive_transform(const T& input, const F& f)
{
    if constexpr (unwrap_level > 0)
    {
        recursive_invoke_result_t<F, T> 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<unwrap_level - 1>(element, f); }
        );
        return output;
    }
    else
    {
        return f(input);
    }
}

Тестовые кейсы

//  non-nested input test, lambda function applied on input directly
int test_number = 3;
std::cout << recursive_transform<0>(test_number, [](auto&& element) { return element + 1; }) << std::endl;

//  nested input test, lambda function applied on input directly
std::vector<int> test_vector = {
    1, 2, 3
};
std::cout << recursive_transform<0>(test_vector, [](auto element)
    {
        element.push_back(4);
        element.push_back(5);
        return element;
    }).size() << std::endl;

//  std::vector<int> -> std::vector<std::string>
auto recursive_transform_result = recursive_transform<1>(
    test_vector,
    [](int x)->std::string { return std::to_string(x); }
);                                                                                  //  For testing

std::cout << "std::vector<int> -> std::vector<std::string>: " +
    recursive_transform_result.at(0) << std::endl;                                  //  recursive_transform_result.at(0) is a std::string

//  std::vector<string> -> std::vector<int>
std::cout << "std::vector<string> -> std::vector<int>: " 
    << recursive_transform<1>(
        recursive_transform_result,
        [](std::string x) { return std::atoi(x.c_str()); }).at(0) + 1 << std::endl; //  std::string element to int

//  std::vector<std::vector<int>> -> std::vector<std::vector<std::string>>
std::vector<decltype(test_vector)> test_vector2 = {
    test_vector, test_vector, test_vector
};

auto recursive_transform_result2 = recursive_transform<2>(
    test_vector2,
    [](int x)->std::string { return std::to_string(x); }
);                                                                                  //  For testing

std::cout << "string: " + recursive_transform_result2.at(0).at(0) << std::endl;     // recursive_transform_result.at(0).at(0) is also a std::string

//  std::deque<int> -> std::deque<std::string>
std::deque<int> test_deque;
test_deque.push_back(1);
test_deque.push_back(1);
test_deque.push_back(1);

auto recursive_transform_result3 = recursive_transform<1>(
    test_deque,
    [](int x)->std::string { return std::to_string(x); });                          //  For testing

std::cout << "string: " + recursive_transform_result3.at(0) << std::endl;

//  std::deque<std::deque<int>> -> std::deque<std::deque<std::string>>
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);

auto recursive_transform_result4 = recursive_transform<2>(
    test_deque2,
    [](int x)->std::string { return std::to_string(x); });                          //  For testing

std::cout << "string: " + recursive_transform_result4.at(0).at(0) << std::endl;

//  std::list<int> -> std::list<std::string>
std::list<int> test_list = { 1, 2, 3, 4 };
auto recursive_transform_result5 = recursive_transform<1>(
    test_list,
    [](int x)->std::string { return std::to_string(x); });                          //  For testing
std::cout << "string: " + recursive_transform_result5.front() << std::endl;


//  std::list<std::list<int>> -> std::list<std::list<std::string>>
std::list<std::list<int>> test_list2 = { test_list, test_list, test_list, test_list };
auto recursive_transform_result6 = recursive_transform<2>(
    test_list2,
    [](int x)->std::string { return std::to_string(x); });                          //  For testing
std::cout << "string: " + recursive_transform_result6.front().front() << std::endl;

Полный код тестирования

Полный код тестирования:

#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_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 (the version with unwrap_level)
template<std::size_t unwrap_level = 1, class T, class F>
constexpr auto recursive_transform(const T& input, const F& f)
{
    if constexpr (unwrap_level > 0)
    {
        recursive_invoke_result_t<F, T> 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<unwrap_level - 1>(element, f); }
        );
        return output;
    }
    else
    {
        return f(input);
    }
}

int main()
{
    //  non-nested input test, lambda function applied on input directly
    int test_number = 3;
    std::cout << recursive_transform<0>(test_number, [](auto&& element) { return element + 1; }) << std::endl;

    //  nested input test, lambda function applied on input directly
    std::vector<int> test_vector = {
        1, 2, 3
    };
    std::cout << recursive_transform<0>(test_vector, [](auto element)
        {
            element.push_back(4);
            element.push_back(5);
            return element;
        }).size() << std::endl;

    //  std::vector<int> -> std::vector<std::string>
    auto recursive_transform_result = recursive_transform<1>(
        test_vector,
        [](int x)->std::string { return std::to_string(x); }
    );                                                                                  //  For testing

    std::cout << "std::vector<int> -> std::vector<std::string>: " +
        recursive_transform_result.at(0) << std::endl;                                  //  recursive_transform_result.at(0) is a std::string
    
    //  std::vector<string> -> std::vector<int>
    std::cout << "std::vector<string> -> std::vector<int>: " 
        << recursive_transform<1>(
            recursive_transform_result,
            [](std::string x) { return std::atoi(x.c_str()); }).at(0) + 1 << std::endl; //  std::string element to int

    //  std::vector<std::vector<int>> -> std::vector<std::vector<std::string>>
    std::vector<decltype(test_vector)> test_vector2 = {
        test_vector, test_vector, test_vector
    };

    auto recursive_transform_result2 = recursive_transform<2>(
        test_vector2,
        [](int x)->std::string { return std::to_string(x); }
    );                                                                                  //  For testing

    std::cout << "string: " + recursive_transform_result2.at(0).at(0) << std::endl;     // recursive_transform_result.at(0).at(0) is also a std::string

    //  std::deque<int> -> std::deque<std::string>
    std::deque<int> test_deque;
    test_deque.push_back(1);
    test_deque.push_back(1);
    test_deque.push_back(1);

    auto recursive_transform_result3 = recursive_transform<1>(
        test_deque,
        [](int x)->std::string { return std::to_string(x); });                          //  For testing

    std::cout << "string: " + recursive_transform_result3.at(0) << std::endl;

    //  std::deque<std::deque<int>> -> std::deque<std::deque<std::string>>
    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);

    auto recursive_transform_result4 = recursive_transform<2>(
        test_deque2,
        [](int x)->std::string { return std::to_string(x); });                          //  For testing

    std::cout << "string: " + recursive_transform_result4.at(0).at(0) << std::endl;

    //  std::list<int> -> std::list<std::string>
    std::list<int> test_list = { 1, 2, 3, 4 };
    auto recursive_transform_result5 = recursive_transform<1>(
        test_list,
        [](int x)->std::string { return std::to_string(x); });                          //  For testing
    std::cout << "string: " + recursive_transform_result5.front() << std::endl;


    //  std::list<std::list<int>> -> std::list<std::list<std::string>>
    std::list<std::list<int>> test_list2 = { test_list, test_list, test_list, test_list };
    auto recursive_transform_result6 = recursive_transform<2>(
        test_list2,
        [](int x)->std::string { return std::to_string(x); });                          //  For testing
    std::cout << "string: " + recursive_transform_result6.front().front() << std::endl;
    return 0;
}

Ссылка Godbolt здесь.

Все предложения приветствуются.

Сводная информация:

  • На какой вопрос это продолжение?

    Реализация функции шаблона recursive_transform с концепцией std :: invocable в C ++ и

    Реализация функции шаблона recursive_transform с recursive_invoke_result_t и std :: range :: transform в C ++

  • Какие изменения были внесены в код с момента последнего вопроса?

    Тип recursive_transform основная идея здесь — функция с параметром уровня разворачивания.

  • Почему запрашивается новый обзор?

    Если есть какие-то улучшения, пожалуйста, дайте мне знать.

0

Добавить комментарий

Ваш адрес email не будет опубликован. Обязательные поля помечены *