web-dev-qa-db-fra.com

C ++ 11: je peux passer de plusieurs arguments à Tuple, mais puis-je passer de Tuple à plusieurs arguments?

Duplicata possible:
Comment puis-je développer un Tuple dans les arguments de la fonction de modèle variadic?
"décompresser" un Tuple pour appeler un pointeur de fonction correspondant

Dans les modèles C++ 11, existe-t-il un moyen d'utiliser un tuple comme arguments individuels d'une fonction (éventuellement un modèle)?

Exemple:
Disons que j'ai cette fonction:

void foo(int a, int b)  
{  
}

Et j'ai le Tuple auto bar = std::make_Tuple(1, 2).

Puis-je utiliser cela pour appeler foo(1, 2) d'une manière modèle?

Je ne veux pas simplement dire foo(std::get<0>(bar), std::get<1>(bar)) puisque je veux le faire dans un modèle qui ne connaît pas le nombre d'arguments.

Exemple plus complet:

template<typename Func, typename... Args>  
void caller(Func func, Args... args)  
{  
    auto argtuple = std::make_Tuple(args...);  
    do_stuff_with_Tuple(argtuple);  
    func(insert_magic_here(argtuple));  // <-- this is the hard part  
}

Je dois noter que je préférerais ne pas créer un modèle qui fonctionne pour un argument, un autre qui fonctionne pour deux, etc…

34
Thomas

Essayez quelque chose comme ceci:

// implementation details, users never invoke these directly
namespace detail
{
    template <typename F, typename Tuple, bool Done, int Total, int... N>
    struct call_impl
    {
        static void call(F f, Tuple && t)
        {
            call_impl<F, Tuple, Total == 1 + sizeof...(N), Total, N..., sizeof...(N)>::call(f, std::forward<Tuple>(t));
        }
    };

    template <typename F, typename Tuple, int Total, int... N>
    struct call_impl<F, Tuple, true, Total, N...>
    {
        static void call(F f, Tuple && t)
        {
            f(std::get<N>(std::forward<Tuple>(t))...);
        }
    };
}

// user invokes this
template <typename F, typename Tuple>
void call(F f, Tuple && t)
{
    typedef typename std::decay<Tuple>::type ttype;
    detail::call_impl<F, Tuple, 0 == std::Tuple_size<ttype>::value, std::Tuple_size<ttype>::value>::call(f, std::forward<Tuple>(t));
}

Exemple:

#include <cstdio>
int main()
{
    auto t = std::make_Tuple("%d, %d, %d\n", 1,2,3);
    call(std::printf, t);
}

Avec un peu de magie supplémentaire et en utilisant std::result_of, vous pouvez probablement aussi faire en sorte que le tout retourne la valeur de retour correcte.

59
Kerrek SB

Créez un "index Tuple" (un tuple d'entiers au moment de la compilation) puis transférez-le vers une autre fonction qui déduit les index en tant que pack de paramètres et les utilise dans une extension de pack pour appeler std::get sur le tuple:

#include <redi/index_Tuple.h>

template<typename Func, typename Tuple, unsigned... I>  
  void caller_impl(Func func, Tuple&& t, redi::index_Tuple<I...>)  
  {  
    func(std::get<I>(t)...);
  }

template<typename Func, typename... Args>  
  void caller(Func func, Args... args)  
  {  
    auto argtuple = std::make_Tuple(args...);  
    do_stuff_with_Tuple(argtuple);
    typedef redi::to_index_Tuple<Args...> indices;
    caller_impl(func, argtuple, indices());
  }

Mon implémentation de index_Tuple est à https://gitlab.com/redistd/redistd/blob/master/include/redi/index_Tuple.h mais il s'appuie sur des alias de modèle, donc si votre compilateur ne prend pas en charge que vous aurais besoin de le modifier pour utiliser des "typedefs de modèle" de style C++ 03 et remplacer les deux dernières lignes de caller par

    typedef typename redi::make_index_Tuple<sizeof...(Args)>::type indices;
    caller_impl(func, argtuple, indices());

Un utilitaire similaire a été normalisé comme std::index_sequence en C++ 14 (voir index_seq.h pour une implémentation C++ 11 autonome).

4
Jonathan Wakely