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 :
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)
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); }
Le premier exemple ne fonctionnerait-il pas avec C ++ 17 si vous utilisez simplement auto &&
?
Ê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...>{}); }
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); }
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
peut-être stackoverflow.com/questions/1198260/...