0

J'ai écrit un modèle variadique qui exécute un foncteur F exactement N fois et en accumulant le résultat. Maintenant, je me demande comment rendre ce modèle capable de traiter une quantité variable de paramètres d'index (template) actuellement nommés id et dans le foncteur désiré x, y, z. Ce que j'ai à l'esprit était un foncteur comme ci-dessous qui est exécuté comme si vous l'appeliez trois pour les boucles. Je me demande aussi si cela pourrait être résolu par des listes d'arguments.Fonction de modèle récursif itérant sur plusieurs paramètres de modèle

struct 3DFunctor { 
    template <int x, int y, int z> 
    static int run() { 
     return x*y*z; 
    } 
}; 

Comportement souhaité doit être quelque chose comme:

accum_for<3,3,3>::get<3DFunctor>(); 

3DFunctor::run<0,0,0>(/*args ...*/) + 
3DFunctor::run<0,0,1>(/*args ...*/) + 
3DFunctor::run<0,0,2>(/*args ...*/) + 
3DFunctor::run<0,1,0>(/*args ...*/) + 
3DFunctor::run<0,1,1>(/*args ...*/) + 
3DFunctor::run<0,1,2>(/*args ...*/) + 
// .. 

Exemple:

#include <iostream> 
#include <string> 

struct F { 
    template <int id> 
    static int run(int val) { 
     return id * val; 
    } 
}; 

template<unsigned int n> 
struct accum1d_for { 
    template <class Funct, class ... ArgTypes> 
    static auto get(ArgTypes ... args) -> decltype(Funct::template run<0>(args ...)) { 
     return (
      accum1d_for<n-1>::template get<Funct>(args ...) + 
      Funct::template run<n>(args ...) 
     ); 
    } 
}; 

template<> 
struct accum1d_for<0> { 
    template <class Funct, class ... ArgTypes> 
    static auto get(ArgTypes ... args) -> decltype(Funct::template run<0>(args ...)) { 
     return static_cast<decltype(Funct::template run<0>(args ...))>(0); 
    } 
}; 

int main() 
{ 
    std::cout << accum1d_for<10>::get<F>(2.f) << std::endl; 
} 
+1

Ajouté Comportement désiré – dgrat

+0

a essayé un "using blah =" mais a eu une erreur de compilateur. Je ne sais pas si cela fonctionne dans les corps de la fonction. – dgrat

+1

Quelle version de la langue? C++ 11, C++ 14 ou C++ 17? – max66

Répondre

1

Je propose une solution C++ 14 (en utilisant std::index_sequence et std::make_index_sequence(), si vous avez besoin d'une solution C++ 11 devrait être simple créer un substitut).

J'ai ajouté constexpr à votre get() afin que vous puissiez l'utiliser pour initialiser les valeurs constexpr.

Et j'ai utilisé std::size_t au lieu de int parce que vous avez écrit sur "index", donc c'est un peu plus simple.

L'exemple

#include <utility> 
#include <iostream> 

struct Functor3D 
{ 
    template <std::size_t x, std::size_t y, std::size_t z> 
    static constexpr std::size_t run() 
    { return x*y*z; } 
}; 

template <std::size_t ... topIs> 
struct accum_for 
{ 
    private: 
     template <std::size_t ... Is> 
     using IndS = std::index_sequence<Is...>; 

     template <typename Func, std::size_t ... Is> 
     static constexpr std::size_t h1 (IndS<Is...> const &) 
     { return Func::template run<Is...>(); } 

     template <typename Func, std::size_t ... I0s, 
       std::size_t ... Ins, typename ... Ts> 
     static constexpr std::size_t h1 (IndS<I0s...>, IndS<Ins...>, 
             Ts const & ... ts) 
     { 
     using unused = int[]; 

     std::size_t ret { 0 }; 

     (void)unused { 0, 
      ((void)(ret += h1<Func>(IndS<I0s..., Ins>{}, ts...)), 0)... }; 

     return ret; 
     } 

    public: 
     template <typename Func> 
     static constexpr std::size_t get() 
     { return h1<Func>(IndS<>{}, std::make_index_sequence<topIs>()...); } 
}; 

int main(void) 
{ 
    constexpr std::size_t val { accum_for<3U, 3U, 3U>::get<Functor3D>() }; 

    std::cout << val << std::endl; 
} 

Si vous pouvez utiliser 17 C++, la version générique de la fonction d'aide h1() (l'un avec le tableau unused) peut être simplifiée comme suit

template <typename Func, std::size_t ... I0s, 
      std::size_t ... Ins, typename ... Ts> 
    static constexpr std::size_t h1 (IndS<I0s...>, IndS<Ins...>, 
            Ts const & ... ts) 
    { return (h1<Func>(IndS<I0s..., Ins>{}, ts...) + ...); } 
+0

Cela semble fonctionner mais semble compliqué. Pouvez-vous m'aider à comprendre certaines expressions? a) en utilisant inutilisé = int []; est-ce un alias pour un tableau? b) Pourquoi les initialiseurs de tableau {...} manquent un opérateur =? c) Pourquoi une conversion vide est-elle nécessaire? – dgrat

+0

@dgrat - ça a l'air compliqué mais ce que vous demandez n'est pas simple (mais la version C++ 17 c'est plus simple); (a) oui: c'est un alias pour un tableau; il est utilisé parce que vous ne pouvez pas écrire 'int [] {}' (sans donner de nom à la variable) mais vous pouvez écrire 'unused {}'; (b) il est appelé "initialisation uniforme", introduit à partir de C++ 11; (c) il y a deux "conversions nulles"; le premier ('(void) unused') n'est pas nécessaire mais utile pour éviter un avertissement de type" valeur inutilisée "(ou similaire); le second ('(void) (ret + = ...') il est nécessaire d'éviter un bug dans certaines versions de clang ++ – max66

3

Avec std::index_sequence, vous pouvez faire

template <std::size_t N1, std::size_t N2, std::size_t N3> 
struct accum_for 
{ 
private: 
    template <class Funct, std::size_t ... Is> 
    static int get(std::index_sequence<Is...>) { 
     int res = 0; 
     using int_array = int[]; 
     static_cast<void>(int_array{0, (res += Funct::template run<Is/N3/N2, 
                    (Is/N3) % N2, 
                    Is % N3>())...}); 
     return res; 
    } 


public: 
    template <class Funct> 
    static int get() { 
     return get<Funct>(std::make_index_sequence<N1 * N2 * N3>()); 
    } 
}; 

Demo

+0

Merveilleuse solution pour 3 index (+1) mais l'OP demande une solution "template capable de traiter une quantité variable de paramètres d'index (template)" – max66

1

Étant donné n nples, générez leur produit croisé (le tuple d'un élément de chaque, où chaque combinaison est couverte). Étant donné un ensemble de constantes intégrales, appelez run avec leurs valeurs.

Pour une valeur donnée, générer un couple de constantes intégrales de 0 à n-1. Pour un tuple, exécutez un objet fonction sur chaque élément.

3 et 1 définit le cube sur lequel vous voulez exécuter.

passe ce cube à 4, avec un lambda qui fait 2.

Bien qu'il woukd amusant d'écrire, pas la peine de remplacer 9 lignes de code. Aucune des opérations ci-dessus n'est préécrite dans std; Je suppose que la plupart des bibliothèques de métaprogrammation les auraient implémentées. Il faudrait moins de 50 lignes pour chacune des étapes ci-dessus pour écrire à partir de zéro en utilisant std (peut-être beaucoup moins).