Как сделать рекурсию на основе элементов кортежа? - PullRequest
1 голос
/ 20 июня 2020

Я не уверен, возможно ли это, но я хотел бы иметь возможность рекурсивно вызывать функцию на основе элементов кортежа. Так, например, кортеж, такой как std::tuple<int,float,double>, должен вызывать expand_nested 3 раза и, в свою очередь, вызывать его функцию обратного вызова с параметром типа int, float или double.

#include <tuple>
#include <vector>
#include <functional>

template <typename T>
struct tree_item
{
    T param;
    std::function<void(T)> callback;
};

template <typename... Ts>
struct tuple_node
{
    std::tuple<Ts...> tupl;
};

// recursion base case
template <typename T>
void expand_nested(tree_item<T> ti)
{
    ti.callback(ti.param);
}

// recursive function
template <typename T>
void expand_nested(tree_item<T> ti, tree_item<T> rest...)
{
    ti.callback(ti.param);
    expand_nested(ti, rest...);
}

template <typename... Ts>
void expand_root(tuple_node<Ts...> nodes)
{
    auto current = std::get<1>(nodes.tupl);
    auto rest = std::get<...>(nodes.tupl); // Made up syntax that doesn't work
    // How can I fill the "rest" variable with the remaining elements of the "nodes.tupl" tuple?

    expand_nested(current, rest...);
}

int main()
{
    tuple_node<tree_item<int>, tree_item<float>> nodes;

    tree_item<int> tree_int;
    tree_item<float> tree_float;
    tree_item<double> tree_double;

    tuple_node<tree_item<int>, tree_item<float>, tree_item<double>> node;
    node.tupl = std::make_tuple(tree_int, tree_float, tree_double);

    expand_root(nodes);
}

Ответы [ 4 ]

1 голос
/ 20 июня 2020

Я не уверен, нужны ли вам ваши функции расширения для чего-то еще, но с C ++ 17 вы можете сделать это в однострочном режиме с помощью std::apply.

#include <tuple>
#include <vector>
#include <functional>

template <typename T>
struct tree_item
{
    T param;
    std::function<void(T)> callback;
};

template <typename... Ts>
struct tuple_node
{
    std::tuple<Ts...> tupl;
};

int main()
{
    tuple_node<tree_item<int>, tree_item<float>> nodes;

    tree_item<int> tree_int;
    tree_item<float> tree_float;
    tree_item<double> tree_double;

    tuple_node<tree_item<int>, tree_item<float>, tree_item<double>> node;
    node.tupl = std::make_tuple(tree_int, tree_float, tree_double);

    std::apply([](auto&&... args){
        (args.callback(args.param), ...);
    }, node.tupl);
}
1 голос
/ 20 июня 2020

Это довольно просто в C ++ 20:

#include <iostream>
#include <tuple>


template<typename T>
void func(T t)
{
    std::cout << t << std::endl;
}

int main()
{
    std::tuple<int,float,double> f{1,2,3};

    std::apply([]<typename ...Args>(Args && ...args)
           {
               (func(args), ...);
           }, f);
}

Это требует немного больше работы, чтобы поддерживать только C ++ 11 или выше:

#include <iostream>
#include <tuple>
#include <utility>

template<typename T>
void func(T t)
{
    std::cout << t << std::endl;
}

template<typename T, size_t s, size_t n=0>
struct call_func {

    static void doit(T &t)
    {
        func(std::get<n>(t));

        call_func<T, s, n+1>::doit(t);
    }
};

template<typename T, size_t s>
struct call_func<T, s, s> {

    static void doit(T &t)
    {
    }
};

template<typename ...Args>
void do_callfunc(std::tuple<Args...> &t)
{
    call_func<std::tuple<Args...>, sizeof...(Args), 0>::doit(t);
}

int main()
{
    std::tuple<int,float,double> f{1,2,3};

    do_callfunc(f);
}
1 голос
/ 20 июня 2020

Синтаксис для пакета параметров в expand_nested должен быть:

template <typename T, typename ... Rest>
void expand_nested(tree_item<T> ti, tree_item<Rest>... rest)

Это

ti.callback(ti.param);
expand_nested(ti, rest...);

даст вам бесконечную рекурсию (вы вызываете ту же функцию с тем же количеством аргументы первого вызова), он должен выглядеть так:

template <typename T, typename ... Rest>
void expand_nested(tree_item<T> ti, tree_item<Rest>... rest)
{
    ti.callback(ti.param);    // process ti
    expand_nested(rest...);   // don't pass ti, pass only the rest to further processing
}

Начиная с C ++ 17 существует простой способ извлечь все элементы кортежа - используйте std::apply:

template <typename... Ts>
void expand_root(tuple_node<Ts...> nodes)
{
    std::apply([](auto&... tupleItems){ 
        expand_nested(tupleItems...); }, nodes.tupl);
}

Полная демонстрация

0 голосов
/ 20 июня 2020

Вы уверены, что вам нужна рекурсия?

Если вы можете использовать C ++ 17, как насчет сворачивания шаблонов?

template <typename... Ts, std::size_t ... Is>
void expand_root_helper (tuple_node<Ts...> nodes, std::index_sequence<Is...>)
 { ((void)std::get<Is>(nodes.tupl).callback(
             std::get<Is>(nodes.tupl).param), ...); }

template <typename... Ts>
void expand_root (tuple_node<Ts...> nodes)
 { expand_root_helper(nodes, std::index_sequence_for<Ts...>{}); }

В C ++ 11 / C ++ 14 есть немного сложнее (без сворачивания шаблона), но ваша вспомогательная функция может эмулировать его следующим образом:

template <typename... Ts, std::size_t ... Is>
void expand_root_helper (tuple_node<Ts...> nodes, std::index_sequence<Is...>)
 {
   using unused = int[];

   (void)unused { 0, ((void)std::get<Is>(nodes.tupl).callback(
                               std::get<Is>(nodes.tupl).param), 0)... };
 }
Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...