1
votes

Comment faire une récursion basée sur des éléments de tuple?

Je ne sais pas si cela est possible, mais j'aimerais pouvoir appeler de manière récursive une fonction basée sur les éléments d'un tuple. Ainsi, par exemple, un tuple tel que std::tuple devrait appeler expand_nested 3 fois, et à son tour invoquer sa fonction de rappel avec un paramètre de type soit int , float ou 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 Réponses :


1
votes

La syntaxe du pack de paramètres dans expand_nested doit être:

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

Ceci

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
}

vous donnera infini récursivité (vous appelez la même fonction avec le même nombre d'arguments du premier appel), cela devrait ressembler à:

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

Depuis C ++ 17, il existe un moyen facile de extraire tous les éléments du tuple - utilisez std::apply:

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

Démo complète


0 commentaires

1
votes

C'est assez simple en C ++ 20:

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

Cela demande un peu plus de travail, pour ne prendre en charge que C ++ 11, ou supérieur:

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


1 commentaires

Le premier exemple ne fonctionnerait-il pas avec C ++ 17 si vous utilisez simplement auto && ?



0
votes

Êtes-vous sûr d'avoir besoin de la récursivité?

Si vous pouvez utiliser C ++ 17, qu'en est-il du pliage de modèles?

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

En C ++ 11 / C + +14 est un peu plus compliqué (pas de pliage de modèle) mais votre fonction d'assistance peut l'émuler comme suit

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...>{}); }


0 commentaires

1
votes

Je ne suis pas sûr que vous ayez besoin de vos fonctions d'expansion pour autre chose, mais à partir de C ++ 17, vous pouvez le faire en une seule ligne avec 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 commentaires

C'est probablement la réponse la plus directe au titre de la question, mais dans mon cas, j'ai besoin de l'étape supplémentaire consistant à appeler expand_nested