2012-01-11 1 views
6

Je voudrais écrire un modèle de fonction, apply, qui reçoit une certaine fonction f, un entier i, et un pack de paramètres. apply doit décompresser les paramètres et leur appliquer f, à l'exception du paramètre i, pi. Pour pi, il doit appeler une autre fonction g avant de passer en paramètre à f.Comment partitionner un pack de paramètres?

Il semble que j'ai besoin d'un moyen de partitionner le pack de paramètres dans un côté gauche, le i ème paramètre, et le côté droit. Est-ce possible? Dans le code:

template<int i, typename Function, typename... Parms> 
    void apply(Function f, Parms... parms) 
{ 
    auto lhs = // what goes here? 
    auto pi = // what goes here? 
    auto rhs = // what goes here? 

    f(lhs..., g(pi), rhs...); 
} 
+2

pi est disponible en tant que M_PI en math.h. Pas sûr du reste, pouvez-vous poster du code avec moins de points de suspension? –

+2

@HansPassant Je pense que OP signifie 'ith-parameter' par' pi' –

+5

@HansPassant C++ est maintenant allé tout le chemin: vous n'avez plus besoin d'écrire tout le code! Les ellipses sont en fait du code et sont en cours d'exécution! –

Répondre

3

OK, c'est parti! Il vraiment laid mais je ne pouvais pas venir avec une version plus agréable à la hâte;) La plupart des choses est la spécialisation de modèle standard de bog. Le plus gros problème est de créer une liste d'entiers de la bonne taille. Je me rappelle que j'ai trouvé une belle version mais je ne me souviens pas de ce que j'ai fait. Prendre plaisir!

#include <iostream> 
#include <utility> 

// printing the values 
void print_args() {} 
template <typename F> void print_args(F f) { std::cout << f; } 
template <typename F, typename... T> 
void print_args(F f, T... args) 
{ 
    std::cout << f << ", "; 
    print_args(args...); 
} 

// the function object to be called: 
struct Functor 
{ 
    template <typename... T> 
    void operator()(T... args) 
    { 
     std::cout << "f("; 
     print_args(args...); 
     std::cout << ")\n"; 
    } 
}; 

// conditionally apply g(): 
template <typename T> T g(T value) { return 1000 + value; } 
template <int i, int j, typename T> 
typename std::enable_if<i != j, T>::type forward(T t) { return t; } 
template <int i, int j, typename T> 
typename std::enable_if<i == j, T>::type forward(T t) { return g(t); } 

// create a series of integers: 
template <int... Values> struct values {}; 

template <int Add, typename> struct combine_values; 
template <int Add, int... Values> 
struct combine_values<Add, values<Values...>> 
{ 
    typedef values<Values..., Add> type; 
}; 

template <int Size> struct make_values; 
template <> struct make_values<0> { typedef values<> type; }; 
template <int Size> 
struct make_values 
{ 
    typedef typename combine_values<Size, typename make_values<Size -1>::type>::type type; 
}; 

// applying f(t...) except for ti where g(ti) is called 
template <int i, int... Values, typename Function, typename... T> 
void apply_aux(values<Values...>, Function f, T... t) 
{ 
    f(forward<i, Values>(t)...); 
} 

template <int i, typename Function, typename... T> 
void apply(Function f, T... t) 
{ 
    apply_aux<i>(typename make_values<sizeof...(T)>::type(), f, t...); 
} 

int main() 
{ 
    apply<3>(Functor(), 1, 2, 3, 4, 5, 6, 7, 8); 
    apply<4>(Functor(), 1, 2, 3, 4, 5, 6, 7, 8); 
    apply<5>(Functor(), 1, 2, 3, 4, 5, 6, 7, 8); 
} 
+0

Terminé en utilisant celui-ci. Merci! –

3

De fait, j'ai codé quelque chose de similaire il y a quelque temps. Donc, essayez le code suivant:

template<unsigned N, unsigned M> 
struct call_up_impl{ 
    template<class Func, class Mutator, class Tuple, class... Args> 
    static void do_call(const Func& func, const Mutator& mutator, const Tuple& args, Args&&... unpacked_args) { 
     call_up_impl<N-1, M>::do_call(func, mutator, args, std::get<N-1>(args), std::forward<Args>(unpacked_args)...); 
    } 
}; 

template<unsigned M> 
struct call_up_impl<0, M> { 
    template<class Func, class Mutator, class Tuple, class... Args> 
    static void do_call(const Func& func, const Mutator&, const Tuple&, Args&&... unpacked_args) { 
     func(std::forward<Args>(unpacked_args)...); 
    } 
}; 
template<unsigned M> 
struct call_up_impl<M, M> { 
    template<class Func, class Mutator, class Tuple, class... Args> 
    static void do_call(const Func& func, const Mutator& mutator, const Tuple& args, Args&&... unpacked_args) { 
     call_up_impl<M-1, M>::do_call(func, mutator, args, mutator(std::get<M-1>(args)), std::forward<Args>(unpacked_args)...); 
    } 
}; 
template<int i, typename Function, typename... Parms> 
void apply(Function f, Parms... parms) { 
     std::tuple<Parms...> t(parms...); 
     call_up_impl<std::tuple_size<decltype(t)>::value, i + 1>::do_call(f, &g, t); 
} 

Ceci est une adaptation rapide de mon code d'origine, il est donc pas testé à fond et peut-être pas la façon pas optimale pour ce faire, mais il devrait fonctionner au moins (au moins selon un test rapide et en fonction de ce que vous voulez exactement). Il devrait être possible de le faire sans le tuple, mais je n'ai pas réussi à le compiler avec g ++ (il ne semble pas aimer les templates variés imbriqués). Cependant le changement apply à:

template<int i, typename Function, typename... Parms> 
void apply(Function f, Parms&&... parms) { 
     std::tuple<Parms&&...> t(std::forward<Parms>(parms)...); 
     call_up_impl<std::tuple_size<decltype(t)>::value, i + 1>::do_call(f, &g, t); 
} 

probablement éviter la plupart des frais généraux introduite par le tuple. Il serait encore mieux de faire un renvoi correct des résultats des appels std::get, mais je suis trop fatigué pour travailler sur cette écriture maintenant.

+0

Je pense que cela semble plus compliqué et dépend aussi de 'std :: tuple' pour faire tout le travail! Bien sûr, j'utilise aussi une classe standard ('std :: enable_if') mais comparée à' std :: tuple' est l'implémentation de 'std :: enable_if' trivial. Un peu de mon code est également en train d'appliquer l'opération et d'imprimer les résultats. –

+0

@ DietmarKühl: Je suis en désaccord avec la partie la plus compliquée. Et quel est le problème de s'appuyer sur 'std :: tuple', si nous sommes liés à C++ 11 uniquement en raison de l'utilisation de modèles variadiques?(en plus des modèles variés implémentant les opérations de tuple nécessaires est assez facile aussi). Alors quel est le problème? – Grizzly

+0

Les deux solutions sont intéressantes. Merci de les avoir fournis. Pourtant, je suis déçu qu'il n'y ait pas de solution plus directe dans C++ 11. –

Questions connexes