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/...