2010-03-04 7 views
14

Je me demande comment obtenir quelque chose comme ceci:Comment dérouler une courte boucle en C++

  1. Ecrire

    copy(a, b, 2, 3) 
    
  2. Et puis obtenir

    a[2] = b[2]; 
    a[3] = b[3]; 
    a[4] = b[4]; 
    

I sachez que C#defines ne peut pas être utilisé récursivement pour obtenir cet effet. Mais j'utilise C++, donc je suppose que la méta-programmation de template pourrait être appropriée.

Je sais qu'il existe une bibliothèque Boost pour cela, mais je ne veux que cette "simple" astuce, et Boost est trop "désordonné".

+3

sont vos mesures avant et après des performances assez différentes que c'est le lieu de concentrer vos efforts ? – Bill

+14

@Bill: oui, parce que nous ne voulons pas que les gens vont juste apprendre des techniques d'apprentissage pour le bien de "l'éducation" ou de la "curiosité" ;-) –

+0

En fait, vous pouvez accomplir ceci en utilisant le préprocesseur boost. J'ai fait quelque chose de similaire pour implémenter la sémantique de python unpack en C++, ie 'déballer ((const int a, b, c), abcd);' – Anycorn

Répondre

26

La solution la plus simple pour cela est d'écrire une boucle où les valeurs de début et de fin sont connus:

for(int i = 2; i <= 4; i++) { 
    a[i]=b[i]; 
} 

Je pense que cela vaut mieux que toute sorte de mélange appel d'exécution modèle /: La boucle telle qu'elle est écrite est complètement claire pour l'optimiseur des compilateurs, et il n'y a pas de niveaux d'appels de fonction à explorer juste pour voir ce qui se passe.

+5

De plus, de nombreux compilateurs dérouleront cette boucle lorsque l'optimisation est activée. –

+0

Oui je suis d'accord! Et il est trivial d'écrire une définition à utiliser comme copy (a, b, 2,4) – cibercitizen1

+28

Ceci est la réponse admise? '" comment dérouler une boucle avec des modèles "' et la réponse est n'essayez même pas? Peut-être que vous êtes compilateur va dérouler cela mais pas tous. – Inverse

1

De http://www.sgi.com/tech/stl/Vector.html:

template <class InputIterator> 
vector(InputIterator, InputIterator) 

Creates a vector with a copy of a range. 
+2

Je suis désolé, mais non. Je cherche quelque chose d'aussi proche que possible d'une recherche -copie (a, b, 2,3) - et remplace par un [2] = b [2]; a [3] = b [3]; a [4] = b [4]; – cibercitizen1

22

C++ méta-programmation est récursive.

Pensez à votre problème en termes de récursivité. Implémenter des cas terminaux et des cas non terminaux.

Votre boîtier de terminal peut être 0 ou un. Passez les limites en tant que paramètres de modèle. Utilisez une structure/classe car ils permettent une spécialisation partielle et d'autres choses intéressantes:

template<int from, int to> 
struct copy { 
    static void apply(source, destination) { 
     source[from] = destination[from]; 
     copy<from+1,to>:: apply(source, destination); 
    } 
}; 

// Terminal case 
template<int from> 
struct copy<from, from> { 
    static void apply(source, destination) { 
     source[from] = destination[from]; 
    } 
}; 
+1

'source' et' destination' doivent être déclarés; cela ne compile pas en l'état. –

+1

Hé, c'est le même code que j'ai donné à ma question: http://stackoverflow.com/questions/2380143/c-metaprograming-with-templates-versus-inlining Est-ce que cela signifie, cela est le seul moyen de faites-le * en utilisant des modèles *. – cibercitizen1

+1

@CharlesBailey Ummm ... 'source' et' destination' sont des arguments? –

3

Vous pouvez probablement faire quelque chose comme ce qui suit. Selon votre compilateur et les paramètres d'optimisation que vous utilisez pouvez obtenir l'effet que vous recherchez. Sachez que pour les petits objets tels que char, il peut être plus lent qu'un std::copy ou un memcpy et que pour les objets plus gros, le coût d'une boucle risque d'être insignifiant par rapport aux copies en cours dans tous les cas.

#include <cstddef> 

template<std::size_t base, std::size_t count, class T, class U> 
struct copy_helper 
{ 
    static void copy(T dst, U src) 
    { 
     dst[base] = src[base]; 
     copy_helper<base + 1, count - 1, T, U>::copy(dst, src); 
    } 
}; 

template<std::size_t base, class T, class U> 
struct copy_helper<base, 0, T, U> 
{ 
    static void copy(T, U) 
    { 
    } 
}; 

template<std::size_t base, std::size_t count, class T, class U> 
void copy(T dst, U src) 
{ 
    copy_helper<base, count, T, U>::copy(dst, src); 
} 

template void copy<5, 9, char*, const char*>(char*, const char*); 

#include <iostream> 
#include <ostream> 

int main() 
{ 
    const char test2[] = "  , World\n"; 
    char test[14] = "Hello"; 

    copy<5, 9>(test, test2); 

    std::cout << test; 

    return 0; 
} 
+0

Hey, c'est le même code que j'ai donné dans ma question: stackoverflow.com/questions/2380143/... Cela signifie-t-il, c'est la seule façon de le faire en utilisant des modèles? Je commence à le croire. – cibercitizen1

+0

@ cibercitizen1: J'ai écrit cela indépendamment, mais à un modèle bien connu. –

2

Il est important de réaliser que le compilateur est très intelligent, et que le tromper pour dérouler les boucles en utilisant la métaprogrammation du template vous rappellera probablement qu'il vous fera avancer.

Pour tirer le meilleur parti de vos optimisations: gardez un œil sur le démontage. Cela vous apprendra peut-être plus que de lancer des modèles sur le problème. Notez, comme Johannes a dit: si le compilateur peut voir que vous exécutez une boucle pour un nombre fixe de fois (ou un multiple fixe de fois comme variable 4x), il peut créer un code très proche de l'optimal.

1

Il n'utilise pas de modèles et ce n'est pas un « complet » le déroulement, mais vous pouvez partiellement déroulez la boucle avec quelque chose comme ceci:

void copy (SomeType* a, SomeType* b, int start_index, int num_items) { 
    int i = start_index; 

    while (num_items > 4) { 
      a[i+0] = b[i+0]; 
      a[i+1] = b[i+1]; 
      a[i+2] = b[i+2]; 
      a[i+3] = b[i+3]; 
      i += 4; 
      num_items -= 4; 
    } 
    while (num_items > 0) { 
      a[i] = b[i]; 
      ++i; 
      --num_items; 
    } 
} 

maintenant dans cet exemple particulier, les calculs supplémentaires impliqués sera probablement l'emportent sur les avantages de ne dérouler que quatre éléments à la fois. Vous devriez tirer un avantage croissant d'un nombre croissant d'éléments dans la boucle supérieure (dans toute la fonction, remplacez 4 par de nombreux éléments que vous copiez dans chaque itération déroulée manuellement).

1
template <int begin, int end> struct copy_; 

template <int end> struct copy_<end, end> { 
    template <typename T> static void execute(T& a, T& b) 
    { 
    a[end] = b[end]; 
    } 
}; 

template <int begin, int end> struct copy_<begin, end> { 
    template <typename T> static void execute(T& a, T& b) 
    { 
    a[begin] = b[begin]; 
    copy_<begin+1, end>::execute(a, b); 
    } 
}; 

template <int begin, int how_many> struct copy { 
    template <typename T> static void execute(T& a, T& b) 
    { 
    copy_<begin, begin+how_many-1>::execute(a, b); 
    } 
}; 

copy<2, 3>::execute(a, b);
+0

Hey, c'est le même code que j'ai donné dans ma question: stackoverflow.com/questions/2380143/... Cela signifie-t-il, c'est la seule façon de le faire en utilisant des modèles? C'est le 3ème code dans cette direction, donc ça semble clair. – cibercitizen1

+0

@ cibercitizen1 Na, tout ce que cela signifie, c'est que les gens sont trop paresseux pour faire autre chose qu'une recherche et de faire une question hors de la réponse .... – Archival

1

Le déroulement de la boucle en utilisant la méta-programmation peut être utilisé pour créer constexpr (je n'ai pas mesuré les temps). J'ai un exemple où il peut être utilisé pour faire la combinaison de la fonction (n, k) cosntexpr:

template <size_t N> struct iterate_forward { 
    template <class operation> static auto eval(const operation & op) 
    { 
     iterate_forward<N-1>::eval(op); 
     return op(N-1); 
    } 
}; 
template <> struct iterate_forward<1> 
{ 
    template <class operation> static auto eval(const operation & op) 
    { return op(0); } 
}; 
template <> struct iterate_forward<0> 
{ 
    template <class operation> static auto eval(const operation & op) {} 
}; 
template <size_t N, size_t K> constexpr size_t COMB() 
{ 
    struct ratio 
    { 
     size_t operator() (size_t i) const { return (res *= (N-i)/(i+1)); } 
     mutable size_t res = 1; 
    }; 
    return iterate_forward<K>::eval(ratio()); 
} 
0
#define tl template 
#define tn typename 
#define st struct 
#define cl class 
#define us using 


    template<tn A> st Typ { using type = A; }; 
    tl<tn T> using GetT = tn T::type; 
    tl<tn F, tn ...As> us apply = tn F::tl apply<As...>; 
    tl<tn, tn, tn ...> st LoopElements; 
    tl<tn, tn> st Loop; 
    tl<tn, tn, tn> st VLoop_i; 
    tl<tn Sq, tn MF> us VLoop = VLoop_i<GetT<Sq>, Sq, MF>; 

    // 
    // TY 
    // 
    template<tn T> struct Ty { 
     template<T ...> struct Pack : Typ<T> {}; 
     tl<tn ...> st Concat_i; tl<tn ...P> us Concat = GetT<Concat_i<P...>>; 
     tl<T, int64_t> st Seq_i; tl<T f, T t> us Seq = GetT<Seq_i<f, ((int64_t)(t - f))>>; tl<int64_t, tn> st add; 

     template<tl<T ...> tn C, T ...vs, tl<T ...> tn D, T ...ws, tn ...R> st Concat_i<C<vs...>, D<ws...>, R...> : Typ<Concat<C<vs..., ws...>, R...> >{}; 
     template<tl<T ...> tn C, T ...vs> st Concat_i<C<vs...>> : Typ<C<vs...> >{}; 

     template<int64_t x, T ...vs> struct add<x, Pack<vs...>> : Typ<Pack<((T)(vs + x))...>> {}; 
     template<T f, int64_t c> class Seq_i { 
      using A = tn Seq_i<f, c/2>::type; 
      using B = tn add<c/2, A>::type; 
      using C = tn Seq_i<f + c/2 * 2, c & 1>::type; 
     public: 
      using type = Concat<A, B, C>; 
     }; 
     tl<T f> st Seq_i<f, 0> : Typ<Pack<>> {};   
     tl<T f> st Seq_i<f, 1> : Typ<Pack<f>> {}; 
     tl<T f> st Seq_i<f, -1> : Typ<Pack<f>> {}; 
    }; 

    // 
    // LOOP 
    template<size_t i, tn F, tn T> struct LoopElement { LoopElement() { apply<F, T>(); }; }; 
    template<size_t ...is, tn F, tn ...P> struct LoopElements<Ty<size_t>::Pack<is...>, F, P...> : LoopElement<is, F, P>... {}; 
    template<tn F, tl<tn ...> cl C, tn ...P> struct Loop<F, C<P...>> : LoopElements<Ty<size_t>::Seq<0, sizeof...(P)>, F, P...> {}; 

    template<tn T, tl<T ...> cl ST, T ...vs, tn MF> struct VLoop_i<T, ST<vs...>, MF> : LoopElements<Ty<size_t>::Seq<0, sizeof...(vs)>, MF, Val<T, vs>...> {}; 
+3

Bienvenue sur Stack Overflow! Bien que cet extrait de code puisse résoudre la question, [y compris une explication] (// meta.stackexchange.com/questions/114762/explaining-entirely-code-based-answers) aide vraiment à améliorer la qualité de votre message. Rappelez-vous que vous répondez à la question pour les lecteurs dans le futur, et que ces personnes pourraient ne pas connaître les raisons de votre suggestion de code. Essayez également de ne pas surcharger votre code avec des commentaires explicatifs, ceci réduit la lisibilité du code et des explications! – kayess

Questions connexes