web-dev-qa-db-fra.com

Comment développer un tuple dans les arguments de la fonction de modèle variadique?

Prenons le cas d’une fonction basée sur un modèle avec des arguments de modèles variadiques:

template<typename Tret, typename... T> Tret func(const T&... t);

Maintenant, j'ai un tuple t de valeurs. Comment appeler func() en utilisant les valeurs Tuple comme arguments? J'ai lu des informations sur l'objet fonction bind(), avec la fonction call(), ainsi que sur la fonction apply() dans différents documents obsolètes. L'implémentation GNU GCC 4.4 semble avoir une fonction call() dans la classe bind(), mais il existe très peu de documentation sur le sujet.

Certaines personnes suggèrent des hacks récursifs écrits à la main, mais la vraie valeur des arguments de modèles variadiques est de pouvoir les utiliser dans des cas similaires à ceux décrits ci-dessus.

Quelqu'un a-t-il une solution ou est-il un indice sur les endroits où lire?

119
Gustaf

Voici mon code si quelqu'un est intéressé

Au moment de la compilation, le compilateur déroulera tous les arguments de manière récurrente dans divers appels de fonction inclusifs <N> -> appels <N-1> -> appels ... -> appels <0>, le dernier, et le compilateur optimisera les différents appels de fonction intermédiaires ne gardent que le dernier qui est l'équivalent de func (arg1, arg2, arg3, ...)

Deux versions sont fournies: une pour une fonction appelée sur un objet et l'autre pour une fonction statique.

#include <tr1/Tuple>

/**
 * Object Function Tuple Argument Unpacking
 *
 * This recursive template unpacks the Tuple parameters into
 * variadic template arguments until we reach the count of 0 where the function
 * is called with the correct parameters
 *
 * @tparam N Number of Tuple arguments to unroll
 *
 * @ingroup g_util_Tuple
 */
template < uint N >
struct apply_obj_func
{
  template < typename T, typename... ArgsF, typename... ArgsT, typename... Args >
  static void applyTuple( T* pObj,
                          void (T::*f)( ArgsF... ),
                          const std::tr1::Tuple<ArgsT...>& t,
                          Args... args )
  {
    apply_obj_func<N-1>::applyTuple( pObj, f, t, std::tr1::get<N-1>( t ), args... );
  }
};

//-----------------------------------------------------------------------------

/**
 * Object Function Tuple Argument Unpacking End Point
 *
 * This recursive template unpacks the Tuple parameters into
 * variadic template arguments until we reach the count of 0 where the function
 * is called with the correct parameters
 *
 * @ingroup g_util_Tuple
 */
template <>
struct apply_obj_func<0>
{
  template < typename T, typename... ArgsF, typename... ArgsT, typename... Args >
  static void applyTuple( T* pObj,
                          void (T::*f)( ArgsF... ),
                          const std::tr1::Tuple<ArgsT...>& /* t */,
                          Args... args )
  {
    (pObj->*f)( args... );
  }
};

//-----------------------------------------------------------------------------

/**
 * Object Function Call Forwarding Using Tuple Pack Parameters
 */
// Actual apply function
template < typename T, typename... ArgsF, typename... ArgsT >
void applyTuple( T* pObj,
                 void (T::*f)( ArgsF... ),
                 std::tr1::Tuple<ArgsT...> const& t )
{
   apply_obj_func<sizeof...(ArgsT)>::applyTuple( pObj, f, t );
}

//-----------------------------------------------------------------------------

/**
 * Static Function Tuple Argument Unpacking
 *
 * This recursive template unpacks the Tuple parameters into
 * variadic template arguments until we reach the count of 0 where the function
 * is called with the correct parameters
 *
 * @tparam N Number of Tuple arguments to unroll
 *
 * @ingroup g_util_Tuple
 */
template < uint N >
struct apply_func
{
  template < typename... ArgsF, typename... ArgsT, typename... Args >
  static void applyTuple( void (*f)( ArgsF... ),
                          const std::tr1::Tuple<ArgsT...>& t,
                          Args... args )
  {
    apply_func<N-1>::applyTuple( f, t, std::tr1::get<N-1>( t ), args... );
  }
};

//-----------------------------------------------------------------------------

/**
 * Static Function Tuple Argument Unpacking End Point
 *
 * This recursive template unpacks the Tuple parameters into
 * variadic template arguments until we reach the count of 0 where the function
 * is called with the correct parameters
 *
 * @ingroup g_util_Tuple
 */
template <>
struct apply_func<0>
{
  template < typename... ArgsF, typename... ArgsT, typename... Args >
  static void applyTuple( void (*f)( ArgsF... ),
                          const std::tr1::Tuple<ArgsT...>& /* t */,
                          Args... args )
  {
    f( args... );
  }
};

//-----------------------------------------------------------------------------

/**
 * Static Function Call Forwarding Using Tuple Pack Parameters
 */
// Actual apply function
template < typename... ArgsF, typename... ArgsT >
void applyTuple( void (*f)(ArgsF...),
                 std::tr1::Tuple<ArgsT...> const& t )
{
   apply_func<sizeof...(ArgsT)>::applyTuple( f, t );
}

// ***************************************
// Usage
// ***************************************

template < typename T, typename... Args >
class Message : public iMessage
{

  typedef void (T::*F)( Args... args );

public:

  Message( const std::string& name,
           T& obj,
           F pFunc,
           Args... args );

private:

  virtual void doDispatch( );

  T*  pObj_;
  F   pFunc_;
  std::tr1::Tuple<Args...> args_;
};

//-----------------------------------------------------------------------------

template < typename T, typename... Args >
Message<T, Args...>::Message( const std::string& name,
                              T& obj,
                              F pFunc,
                              Args... args )
: iMessage( name ),
  pObj_( &obj ),
  pFunc_( pFunc ),
  args_( std::forward<Args>(args)... )
{

}

//-----------------------------------------------------------------------------

template < typename T, typename... Args >
void Message<T, Args...>::doDispatch( )
{
  try
  {
    applyTuple( pObj_, pFunc_, args_ );
  }
  catch ( std::exception& e )
  {

  }
}
45
David

En C++, il existe de nombreuses façons d’extraire/décompresser Tuple et d’appliquer ces éléments Tuple à une fonction de modèle variadique. Voici une petite classe d'assistance qui crée un tableau d'index. Il est beaucoup utilisé dans la métaprogrammation des templates:

// ------------- UTILITY---------------
template<int...> struct index_Tuple{}; 

template<int I, typename IndexTuple, typename... Types> 
struct make_indexes_impl; 

template<int I, int... Indexes, typename T, typename ... Types> 
struct make_indexes_impl<I, index_Tuple<Indexes...>, T, Types...> 
{ 
    typedef typename make_indexes_impl<I + 1, index_Tuple<Indexes..., I>, Types...>::type type; 
}; 

template<int I, int... Indexes> 
struct make_indexes_impl<I, index_Tuple<Indexes...> > 
{ 
    typedef index_Tuple<Indexes...> type; 
}; 

template<typename ... Types> 
struct make_indexes : make_indexes_impl<0, index_Tuple<>, Types...> 
{}; 

Maintenant, le code qui fait le travail n'est pas si gros:

 // ----------UNPACK Tuple AND APPLY TO FUNCTION ---------
#include <Tuple>
#include <iostream> 

using namespace std;

template<class Ret, class... Args, int... Indexes > 
Ret apply_helper( Ret (*pf)(Args...), index_Tuple< Indexes... >, Tuple<Args...>&& tup) 
{ 
    return pf( forward<Args>( get<Indexes>(tup))... ); 
} 

template<class Ret, class ... Args> 
Ret apply(Ret (*pf)(Args...), const Tuple<Args...>&  tup)
{
    return apply_helper(pf, typename make_indexes<Args...>::type(), Tuple<Args...>(tup));
}

template<class Ret, class ... Args> 
Ret apply(Ret (*pf)(Args...), Tuple<Args...>&&  tup)
{
    return apply_helper(pf, typename make_indexes<Args...>::type(), forward<Tuple<Args...>>(tup));
}

Le test est montré ci-dessous:

// --------------------- TEST ------------------
void one(int i, double d)
{
    std::cout << "function one(" << i << ", " << d << ");\n";
}
int two(int i)
{
    std::cout << "function two(" << i << ");\n";
    return i;
}

int main()
{
    std::Tuple<int, double> tup(23, 4.5);
    apply(one, tup);

    int d = apply(two, std::make_Tuple(2));    

    return 0;
}

Je ne suis pas un grand spécialiste des autres langues, mais je suppose que si ces langues ne disposent pas de telles fonctionnalités dans leur menu, il n’ya aucun moyen de le faire. Au moins avec C++, vous le pouvez, et je pense que ce n’est pas si compliqué ... 

35
sigidagi

Je trouve que c'est la solution la plus élégante (et qu'elle est transmise de manière optimale):

#include <cstddef>
#include <Tuple>
#include <type_traits>
#include <utility>

template<size_t N>
struct Apply {
    template<typename F, typename T, typename... A>
    static inline auto apply(F && f, T && t, A &&... a)
        -> decltype(Apply<N-1>::apply(
            ::std::forward<F>(f), ::std::forward<T>(t),
            ::std::get<N-1>(::std::forward<T>(t)), ::std::forward<A>(a)...
        ))
    {
        return Apply<N-1>::apply(::std::forward<F>(f), ::std::forward<T>(t),
            ::std::get<N-1>(::std::forward<T>(t)), ::std::forward<A>(a)...
        );
    }
};

template<>
struct Apply<0> {
    template<typename F, typename T, typename... A>
    static inline auto apply(F && f, T &&, A &&... a)
        -> decltype(::std::forward<F>(f)(::std::forward<A>(a)...))
    {
        return ::std::forward<F>(f)(::std::forward<A>(a)...);
    }
};

template<typename F, typename T>
inline auto apply(F && f, T && t)
    -> decltype(Apply< ::std::Tuple_size<
        typename ::std::decay<T>::type
    >::value>::apply(::std::forward<F>(f), ::std::forward<T>(t)))
{
    return Apply< ::std::Tuple_size<
        typename ::std::decay<T>::type
    >::value>::apply(::std::forward<F>(f), ::std::forward<T>(t));
}

Exemple d'utilisation:

void foo(int i, bool b);

std::Tuple<int, bool> t = make_Tuple(20, false);

void m()
{
    apply(&foo, t);
}

Malheureusement, GCC (4.6 au moins) ne parvient pas à compiler cela avec "désolé, non implémenté: surcharge lourde" (ce qui signifie simplement que le compilateur n'implémente pas encore complètement la spécification C++ 11), et puisqu'il utilise des modèles variadiques, il ne le fera pas. travailler dans MSVC, donc c'est plus ou moins inutile. Cependant, une fois qu’un compilateur prend en charge les spécifications, ce sera la meilleure approche à mon humble avis. (Remarque: il n'est pas difficile de modifier cela afin de pouvoir contourner les défauts de GCC ou de le mettre en œuvre avec Boost Preprocessor, mais cela gâche l'élégance, c'est donc la version que je publie.)

GCC 4.7 supporte maintenant ce code très bien.

Éditer: Ajout de l’appel de fonction réel vers l’avant pour prendre en charge le formulaire de référence rvalue * ceci au cas où vous utiliseriez Clang (ou si quelqu'un d'autre a réellement décidé de l'ajouter).

Édition: Ajout d'un élément manquant en avant autour de l'objet fonction dans le corps de la fonction non-membre apply. Merci à Pheedbaq de nous avoir signalé sa disparition.

Edit: Et voici la version C++ 14 juste parce qu’elle est bien plus jolie (elle ne compile pas encore):

#include <cstddef>
#include <Tuple>
#include <type_traits>
#include <utility>

template<size_t N>
struct Apply {
    template<typename F, typename T, typename... A>
    static inline auto apply(F && f, T && t, A &&... a) {
        return Apply<N-1>::apply(::std::forward<F>(f), ::std::forward<T>(t),
            ::std::get<N-1>(::std::forward<T>(t)), ::std::forward<A>(a)...
        );
    }
};

template<>
struct Apply<0> {
    template<typename F, typename T, typename... A>
    static inline auto apply(F && f, T &&, A &&... a) {
        return ::std::forward<F>(f)(::std::forward<A>(a)...);
    }
};

template<typename F, typename T>
inline auto apply(F && f, T && t) {
    return Apply< ::std::Tuple_size< ::std::decay_t<T>
      >::value>::apply(::std::forward<F>(f), ::std::forward<T>(t));
}

Voici une version pour les fonctions membres (pas beaucoup testée!):

using std::forward; // You can change this if you like unreadable code or care hugely about namespace pollution.

template<size_t N>
struct ApplyMember
{
    template<typename C, typename F, typename T, typename... A>
    static inline auto apply(C&& c, F&& f, T&& t, A&&... a) ->
        decltype(ApplyMember<N-1>::apply(forward<C>(c), forward<F>(f), forward<T>(t), std::get<N-1>(forward<T>(t)), forward<A>(a)...))
    {
        return ApplyMember<N-1>::apply(forward<C>(c), forward<F>(f), forward<T>(t), std::get<N-1>(forward<T>(t)), forward<A>(a)...);
    }
};

template<>
struct ApplyMember<0>
{
    template<typename C, typename F, typename T, typename... A>
    static inline auto apply(C&& c, F&& f, T&&, A&&... a) ->
        decltype((forward<C>(c)->*forward<F>(f))(forward<A>(a)...))
    {
        return (forward<C>(c)->*forward<F>(f))(forward<A>(a)...);
    }
};

// C is the class, F is the member function, T is the Tuple.
template<typename C, typename F, typename T>
inline auto apply(C&& c, F&& f, T&& t) ->
    decltype(ApplyMember<std::Tuple_size<typename std::decay<T>::type>::value>::apply(forward<C>(c), forward<F>(f), forward<T>(t)))
{
    return ApplyMember<std::Tuple_size<typename std::decay<T>::type>::value>::apply(forward<C>(c), forward<F>(f), forward<T>(t));
}
// Example:

class MyClass
{
public:
    void foo(int i, bool b);
};

MyClass mc;

std::Tuple<int, bool> t = make_Tuple(20, false);

void m()
{
    apply(&mc, &MyClass::foo, t);
}
30
DRayX
template<typename F, typename Tuple, std::size_t ... I>
auto apply_impl(F&& f, Tuple&& t, std::index_sequence<I...>) {
    return std::forward<F>(f)(std::get<I>(std::forward<Tuple>(t))...);
}
template<typename F, typename Tuple>
auto apply(F&& f, Tuple&& t) {
    using Indices = std::make_index_sequence<std::Tuple_size<std::decay_t<Tuple>>::value>;
    return apply_impl(std::forward<F>(f), std::forward<Tuple>(t), Indices());
}

Ceci est adapté du brouillon C++ 14 en utilisant index_sequence. Je pourrais proposer de l'appliquer dans une future norme (TS).

22
PeterSom

En C++ 17, vous pouvez faire ceci:

std::apply(the_function, the_Tuple);

Cela fonctionne déjà dans Clang ++ 3.9, en utilisant std :: experimental :: apply.

En réponse au commentaire indiquant que cela ne fonctionnera pas si the_function est modélisé, voici une solution de contournement:

#include <Tuple>

template <typename T, typename U> void my_func(T &&t, U &&u) {}

int main(int argc, char *argv[argc]) {

  std::Tuple<int, float> my_Tuple;

  std::apply([](auto &&... args) { my_func(args...); }, my_Tuple);

  return 0;
}

Cette solution de contournement est une solution simplifiée au problème général de transmission des ensembles de surcharge et du modèle de fonction dans lequel une fonction est attendue. La solution générale (qui prend en charge le transfert parfait, le constexpr-ness et le noexcept-ness) est présentée ici: https://blog.tartanllama.xyz/passing-overload-sets/ .

19
M. Alaggan

1) si vous avez une structure readymade parameters_pack comme argument de fonction, vous pouvez simplement utiliser std :: tie comme ceci:

template <class... Args>
void tie_func(std::Tuple<Args...> t, Args&... args)
{
 std::tie<Args...>(args...) = t;
}

int main()
{
 std::Tuple<int, double, std::string> t(2, 3.3, "abc");

 int i;
 double d;
 std::string s;

 tie_func(t, i, d, s);

 std::cout << i << " " << d << " " << s << std::endl;
}

2) si vous n'avez pas un argument prêt à l'emploi, vous devrez dérouler le tuple comme ceci

#include <Tuple>
#include <functional>
#include <iostream>



template<int N>
struct apply_wrap {
    template<typename R, typename... TupleArgs, typename... UnpackedArgs>
    static R applyTuple( std::function<R(TupleArgs...)>& f, const std::Tuple<TupleArgs...>& t, UnpackedArgs... args )
    {
        return apply_wrap<N-1>::applyTuple( f, t, std::get<N-1>( t ), args... );
    }
};


template<>
struct apply_wrap<0>
{
    template<typename R, typename... TupleArgs, typename... UnpackedArgs>
    static R applyTuple( std::function<R(TupleArgs...)>& f, const std::Tuple<TupleArgs...>&, UnpackedArgs... args )
    {
        return f( args... );
    }
};



template<typename R, typename... TupleArgs>
R applyTuple( std::function<R(TupleArgs...)>& f, std::Tuple<TupleArgs...> const& t )
{
    return apply_wrap<sizeof...(TupleArgs)>::applyTuple( f, t );
}



int fac(int n)
{
    int r=1;
    for(int i=2; i<=n; ++i)
        r *= i;
    return r;
}



int main()
{
    auto t = std::make_Tuple(5);
    auto f = std::function<decltype(fac)>(&fac);
    cout << applyTuple(f, t);
}
1
sdd

Les nouvelles ne sont pas bonnes.

Après avoir lu le projet de norme qui vient de paraître , je ne vois pas de solution intégrée à cela, ce qui semble étrange.

Le meilleur endroit pour poser de telles questions (si ce n'est déjà fait) est modéré par comp.lang.c ++, car certaines personnes participent régulièrement à la rédaction de la publication standard.

Si vous consultez ce fil , quelqu'un a la même question (c'est peut-être vous, auquel cas vous allez trouver cette réponse un peu frustrante!), Et quelques implémentations grossières sont suggérées.

Je me demandais simplement s'il serait plus simple de faire accepter par la fonction une Tuple, car la conversion est ainsi plus facile. Mais cela implique que toutes les fonctions acceptent les n-uplets comme arguments, pour un maximum de flexibilité et ne font que démontrer l'étrangeté de ne pas fournir une extension intégrée du pack d'arguments Tuple to function.

Mise à jour: le lien ci-dessus ne fonctionne pas - essayez de coller ceci:

http://groups.google.com/group/comp.lang.c++.moderated/browse_thread/thread/750fa3815cdaac45/d8dc09e34bbb9661?lnk=gst&q=Tuple+variadic#d8dc09bb9661

1
Daniel Earwicker

Toutes ces implémentations sont bonnes. Mais en raison de l'utilisation d'un pointeur sur un membre, le compilateur ne peut souvent pas intégrer l'appel de la fonction cible (au moins gcc 4.8 ne peut pas, peu importe ce qui se passe Pourquoi gcc ne peut-il pas déterminer les pointeurs de fonction inline? )

Mais les choses changent si vous envoyez un pointeur à une fonction membre en tant qu'argument de modèle, pas en tant que paramètre de fonction:

/// from https://stackoverflow.com/a/9288547/1559666
template<int ...> struct seq {};
template<int N, int ...S> struct gens : gens<N-1, N-1, S...> {};
template<int ...S> struct gens<0, S...>{ typedef seq<S...> type; };

template<typename TT>
using makeSeq = typename gens< std::Tuple_size< typename std::decay<TT>::type >::value >::type;


// deduce function return type
template<class ...Args>
struct fn_type;

template<class ...Args>
struct fn_type< std::Tuple<Args...> >{

    // will not be called
    template<class Self, class Fn>
    static auto type_helper(Self &self, Fn f) -> decltype((self.*f)(declval<Args>()...)){
        //return (self.*f)(Args()...);
        return NULL;
    }
};

template<class Self, class ...Args>
struct APPLY_Tuple{};

template<class Self, class ...Args>
struct APPLY_Tuple<Self, std::Tuple<Args...>>{
    Self &self;
    APPLY_Tuple(Self &self): self(self){}

    template<class T, T (Self::* f)(Args...),  class Tuple>
    void delayed_call(Tuple &&list){
        caller<T, f, Tuple >(forward<Tuple>(list), makeSeq<Tuple>() );
    }

    template<class T, T (Self::* f)(Args...), class Tuple, int ...S>
    void caller(Tuple &&list, const seq<S...>){
        (self.*f)( std::get<S>(forward<Tuple>(list))... );
    }
};

#define type_of(val) typename decay<decltype(val)>::type

#define apply_Tuple(obj, fname, Tuple) \
    APPLY_Tuple<typename decay<decltype(obj)>::type, typename decay<decltype(Tuple)>::type >(obj).delayed_call< \
            decltype( fn_type< type_of(Tuple) >::type_helper(obj, &decay<decltype(obj)>::type::fname) ), \
            &decay<decltype(obj)>::type::fname \
            > \
            (Tuple);

Et ussage:

struct DelayedCall
{  
    void call_me(int a, int b, int c){
        std::cout << a+b+c;
    }

    void fire(){
        Tuple<int,int,int> list = make_Tuple(1,2,3);
        apply_Tuple(*this, call_me, list); // even simpler than previous implementations
    }
};

Preuve d'inlinable http://goo.gl/5UqVnC


Avec de petites modifications, nous pouvons "surcharger" apply_Tuple:

#define VA_NARGS_IMPL(_1, _2, _3, _4, _5, _6, _7, _8, N, ...) N
#define VA_NARGS(...) VA_NARGS_IMPL(X,##__VA_ARGS__, 7, 6, 5, 4, 3, 2, 1, 0)
#define VARARG_IMPL_(base, count, ...) base##count(__VA_ARGS__)
#define VARARG_IMPL(base, count, ...) VARARG_IMPL_(base, count, __VA_ARGS__)
#define VARARG(base, ...) VARARG_IMPL(base, VA_NARGS(__VA_ARGS__), __VA_ARGS__)

#define apply_Tuple2(fname, Tuple) apply_Tuple3(*this, fname, Tuple)
#define apply_Tuple3(obj, fname, Tuple) \
    APPLY_Tuple<typename decay<decltype(obj)>::type, typename decay<decltype(Tuple)>::type >(obj).delayed_call< \
            decltype( fn_type< type_of(Tuple) >::type_helper(obj, &decay<decltype(obj)>::type::fname) ), \
            &decay<decltype(obj)>::type::fname \
            /* ,decltype(Tuple) */> \
            (Tuple);
#define apply_Tuple(...) VARARG(apply_Tuple, __VA_ARGS__)

...

apply_Tuple(obj, call_me, list);
apply_Tuple(call_me, list);       // call this->call_me(list....)

De plus, c'est la seule solution qui fonctionne avec des fonctions basées sur des modèles.

1
tower120

J'évalue MSVS 2013RC et il n'a pas réussi à compiler certaines des solutions précédentes proposées ici dans certains cas. Par exemple, MSVS ne compilera pas les retours "auto" s'il y a trop de paramètres de fonction, en raison d'une limite d'imbrication d'espace de nom (j'ai envoyé cette information à Microsoft pour qu'elle soit corrigée). Dans d'autres cas, nous avons besoin d'accéder au retour de la fonction, bien que cela puisse également être fait avec une lamda: les deux exemples suivants donnent le même résultat.

apply_Tuple([&ret1](double a){ret1 = cos(a); }, std::make_Tuple<double>(.2));
ret2 = apply_Tuple((double(*)(double))cos, std::make_Tuple<double>(.2));

Et merci encore à ceux qui ont posté des réponses ici avant moi, je n’aurais pas pu y arriver sans cela ... alors voici:

template<size_t N>
struct apply_impl {
    template<typename F, typename T, typename... A>
    static inline auto apply_Tuple(F&& f, T&& t, A&&... a)
    -> decltype(apply_impl<N-1>::apply_Tuple(std::forward<F>(f), std::forward<T>(t),
                          std::get<N-1>(std::forward<T>(t)), std::forward<A>(a)...)) {
         return apply_impl<N-1>::apply_Tuple(std::forward<F>(f), std::forward<T>(t),
                          std::get<N-1>(std::forward<T>(t)), std::forward<A>(a)...);
    }
    template<typename C, typename F, typename T, typename... A>
    static inline auto apply_Tuple(C*const o, F&& f, T&& t, A&&... a)
    -> decltype(apply_impl<N-1>::apply_Tuple(o, std::forward<F>(f), std::forward<T>(t),
                          std::get<N-1>(std::forward<T>(t)), std::forward<A>(a)...)) {
         return apply_impl<N-1>::apply_Tuple(o, std::forward<F>(f), std::forward<T>(t),
                          std::get<N-1>(std::forward<T>(t)), std::forward<A>(a)...);
    }
};

// This is a work-around for MSVS 2013RC that is required in some cases
#if _MSC_VER <= 1800 /* update this when bug is corrected */
template<>
struct apply_impl<6> {
    template<typename F, typename T, typename... A>
    static inline auto apply_Tuple(F&& f, T&& t, A&&... a)
    -> decltype(std::forward<F>(f)(std::get<0>(std::forward<T>(t)), std::get<1>(std::forward<T>(t)), std::get<2>(std::forward<T>(t)),
           std::get<3>(std::forward<T>(t)), std::get<4>(std::forward<T>(t)), std::get<5>(std::forward<T>(t)), std::forward<A>(a)...)) {
         return std::forward<F>(f)(std::get<0>(std::forward<T>(t)), std::get<1>(std::forward<T>(t)), std::get<2>(std::forward<T>(t)),
           std::get<3>(std::forward<T>(t)), std::get<4>(std::forward<T>(t)), std::get<5>(std::forward<T>(t)), std::forward<A>(a)...);
    }
    template<typename C, typename F, typename T, typename... A>
    static inline auto apply_Tuple(C*const o, F&& f, T&& t, A&&... a)
    -> decltype((o->*std::forward<F>(f))(std::get<0>(std::forward<T>(t)), std::get<1>(std::forward<T>(t)), std::get<2>(std::forward<T>(t)),
           std::get<3>(std::forward<T>(t)), std::get<4>(std::forward<T>(t)), std::get<5>(std::forward<T>(t)), std::forward<A>(a)...)) {
         return (o->*std::forward<F>(f))(std::get<0>(std::forward<T>(t)), std::get<1>(std::forward<T>(t)), std::get<2>(std::forward<T>(t)),
           std::get<3>(std::forward<T>(t)), std::get<4>(std::forward<T>(t)), std::get<5>(std::forward<T>(t)), std::forward<A>(a)...);
    }
};
#endif

template<>
struct apply_impl<0> {
    template<typename F, typename T, typename... A>
    static inline auto apply_Tuple(F&& f, T&&, A&&... a)
    -> decltype(std::forward<F>(f)(std::forward<A>(a)...)) {
         return std::forward<F>(f)(std::forward<A>(a)...);
    }
    template<typename C, typename F, typename T, typename... A>
    static inline auto apply_Tuple(C*const o, F&& f, T&&, A&&... a)
    -> decltype((o->*std::forward<F>(f))(std::forward<A>(a)...)) {
         return (o->*std::forward<F>(f))(std::forward<A>(a)...);
    }
};

// Apply Tuple parameters on a non-member or static-member function by perfect forwarding
template<typename F, typename T>
inline auto apply_Tuple(F&& f, T&& t)
-> decltype(apply_impl<std::Tuple_size<typename std::decay<T>::type>::value>::apply_Tuple(std::forward<F>(f), std::forward<T>(t))) {
     return apply_impl<std::Tuple_size<typename std::decay<T>::type>::value>::apply_Tuple(std::forward<F>(f), std::forward<T>(t));
}

// Apply Tuple parameters on a member function
template<typename C, typename F, typename T>
inline auto apply_Tuple(C*const o, F&& f, T&& t)
-> decltype(apply_impl<std::Tuple_size<typename std::decay<T>::type>::value>::apply_Tuple(o, std::forward<F>(f), std::forward<T>(t))) {
     return apply_impl<std::Tuple_size<typename std::decay<T>::type>::value>::apply_Tuple(o, std::forward<F>(f), std::forward<T>(t));
}
0
lap777

En prolongeant la solution de @ David, vous pouvez écrire un modèle récursif qui

  1. N'utilise pas la sémantique (trop verbeuse, imo) integer_sequence
  2. N'utilise pas de paramètre de modèle temporaire supplémentaire int N pour compter les itérations récursives
  3. (Facultatif pour les foncteurs statiques/globaux) utilise le foncteur comme paramètre de modèle pour l'optimisation au moment de la compilation

Par exemple.:

template <class F, F func>
struct static_functor {
    template <class... T, class... Args_tmp>
    static inline auto apply(const std::Tuple<T...>& t, Args_tmp... args)
            -> decltype(func(std::declval<T>()...)) {
        return static_functor<F,func>::apply(t, args...,
                std::get<sizeof...(Args_tmp)>(t));
    }
    template <class... T>
    static inline auto apply(const std::Tuple<T...>& t, T... args)
            -> decltype(func(args...)) {
        return func(args...);
    }
};

static_functor<decltype(&myFunc), &myFunc>::apply(my_Tuple);

Alternativement, si votre foncteur n’est pas défini au moment de la compilation (par exemple, une instance de foncteur non -constexpr ou une expression lambda), vous pouvez l’utiliser comme paramètre de fonction plutôt que comme paramètre de modèle de classe, et en fait, supprimer entièrement la classe qui le contient. :

template <class F, class... T, class... Args_tmp>
inline auto apply_functor(F&& func, const std::Tuple<T...>& t,
        Args_tmp... args) -> decltype(func(std::declval<T>()...)) {
    return apply_functor(func, t, args..., std::get<sizeof...(Args_tmp)>(t));
}
template <class F, class... T>
inline auto apply_functor(F&& func, const std::Tuple<T...>& t,
        T... args) -> decltype(func(args...)) {
    return func(args...);
}

apply_functor(&myFunc, my_Tuple);

Pour les callables pointeur vers fonction membre, vous pouvez ajuster l’un ou l’autre des codes ci-dessus de la même manière que dans la réponse de @ David.

Explication

En référence au second morceau de code, il existe deux fonctions de modèle: la première utilise le foncteur func, la Tuple t avec les types T... et un paquet de paramètres args de types Args_tmp.... Lorsqu'il est appelé, il ajoute de manière récursive les objets de t au pack de paramètres, un à un, du début (0) à la fin, et appelle à nouveau la fonction avec le nouveau pack de paramètres incrémenté.

La signature de la deuxième fonction est presque identique à la première, sauf qu'elle utilise le type T... pour le paramètre pack args. Ainsi, une fois que args dans la première fonction est complètement rempli avec les valeurs de t, son type sera T... (en psuedo-code, typeid(T...) == typeid(Args_tmp...)) et le compilateur appellera donc la deuxième fonction surchargée, qui à son tour appelle func(args...).

Le code de l'exemple de foncteur statique fonctionne de manière identique, le foncteur étant utilisé à la place comme argument de modèle de classe.

0
CrepeGoat

Que dis-tu de ça:

// Warning: NOT tested!
#include <cstddef>
#include <Tuple>
#include <type_traits>
#include <utility>

using std::declval;
using std::forward;
using std::get;
using std::integral_constant;
using std::size_t;
using std::Tuple;

namespace detail
{
    template < typename Func, typename ...T, typename ...Args >
    auto  explode_Tuple( integral_constant<size_t, 0u>, Tuple<T...> const &t,
     Func &&f, Args &&...a )
     -> decltype( forward<Func>(f)(declval<T const>()...) )
    { return forward<Func>( f )( forward<Args>(a)... ); }

    template < size_t Index, typename Func, typename ...T, typename ...Args >
    auto  explode_Tuple( integral_constant<size_t, Index>, Tuple<T...> const&t,
     Func &&f, Args &&...a )
     -> decltype( forward<Func>(f)(declval<T const>()...) )
    {
        return explode_Tuple( integral_constant<size_t, Index - 1u>{}, t,
         forward<Func>(f), get<Index - 1u>(t), forward<Args>(a)... );
    }
}

template < typename Func, typename ...T >
auto  run_Tuple( Func &&f, Tuple<T...> const &t )
 -> decltype( forward<Func>(f)(declval<T const>()...) )
{
    return detail::explode_Tuple( integral_constant<size_t, sizeof...(T)>{}, t,
     forward<Func>(f) );
}

template < typename Tret, typename ...T >
Tret  func_T( Tuple<T...> const &t )
{ return run_Tuple( &func<Tret, T...>, t ); }

Le modèle de fonction run_Tuple prend le tuple donné et transmet ses éléments individuellement à la fonction donnée. Il effectue son travail en appelant de manière récursive ses modèles de fonction d'assistance explode_Tuple. Il est important que run_Tuple passe la taille du tuple à explode_Tuple; Ce nombre sert de compteur pour le nombre d'éléments à extraire.

Si le tuple est vide, alors run_Tuple appelle la première version de explode_Tuple avec la fonction distante comme seul autre argument. La fonction distante est appelée sans arguments et nous avons terminé. Si le tuple n'est pas vide, un nombre plus élevé est passé à la deuxième version de explode_Tuple, avec la fonction distante. Un appel récursif à explode_Tuple est effectué, avec les mêmes arguments, sauf que le nombre de compteurs est réduit de un et (référence au) dernier élément de Tuple utilisé comme argument après la fonction distante. Dans un appel récursif, le compteur n'est pas égal à zéro et un autre appel est effectué avec le compteur diminué à nouveau et l'élément suivant non référencé est inséré dans la liste des arguments après la fonction distante mais avant les autres arguments insérés, ou le compteur atteint zéro et la fonction distante est appelée avec tout les arguments accumulés après celle-ci.

Je ne suis pas sûr d'avoir la syntaxe de forcer une version particulière d'un modèle de fonction. Je pense que vous pouvez utiliser un pointeur sur une fonction en tant qu'objet fonction; le compilateur le corrigera automatiquement.

0
CTMacUser