2

J'écris une sorte de compilateur source à source et à un moment donné, je produis une succession d'instructions if qui me permettent d'instancier l'objet modèle correct. Ces objets nécessitent un bool..., donc certains peuvent avoir juste un, d'autres plusieurs. Il ressemble à:Type de modèle Variadic à partir de valeurs dynamiques

if(unknow_at_compilation==1){ 
    if(unknown2 == 3){ 
     My_obj<true> A; 
     My_obj<true, false> B; 
     /*do something X*/ 
    }else{ 
     My_obj<true> A; 
     My_obj<false, false> B; 
     /*do same thing X*/ 
}else{ 
    if(unknown2 == 3){ 
     My_obj<false> A; 
     My_obj<true, true> B; 
     /*do same thing X*/ 
    }else{ 
     My_obj<false> A; 
     My_obj<false, true> B; 
     /*do same thing X*/ 
    } 
} 

mais avec beaucoup plus de conditions et d'objets. Je ne peux pas utiliser le polymorphisme et le pointeur puisque la performance est un point critique dans mon application et que l'objet A et B sont fortement utilisés.

Au lieu de cette succession complexe si if je voudrais utiliser la métaprogrammation. Cependant, je fais face à de sérieuses difficultés ... Pendant ma génération, je connais le nombre d'objets dont j'ai besoin et combien de "bool" il a besoin.

La première étape a consisté à résumer le « faire quelque chose » part à une structure de modèle prenant ensembles de booléen:

template<bool... val> 
struct bool_set{}; 

template<typename T1, typename T2> 
struct struct_do_something; 

template<bool... b1, bool... b2> 
struct struct_do_something<bool_set<b1...>, bool_set<b2...>> 
{ 
    static void do_something(){ 
     My_obj<b1...> i; 
     My_obj<b2...> j; 
     /*Do something*/ 
    } 

}; 

Ces pièces fonctionne et je peux l'appeler comme ça (par exemple): struct_do_something<bool_set<true, true>, bool_set<false, false>>::do_something();

Ensuite, j'ai écrit une structure qui génère un seul bool_set en utilisant les conditions.

template<bool... static_vals> //bool values of the currently created bool_set 
struct bool_set_generator{ 

    template<typename... Bool> 
    static void select(bool v1, Bool... dynamic_vals) //Conditions in parameters 
    { 
     if(v1) 
      return bool_set_generator<static_vals..., true>::select(dynamic_vals...); 
     else 
      return bool_set_generator<static_vals..., false>::select(dynamic_vals...); 
    } 

    static void select(){ 
     /*I have a bool_set here -> I can "do something"*/ 
     struct_do_something<bool_set<static_vals...>>::do_something(); 
    } 
}; 

De toute évidence, il est pas fini: je peux générer un seul bool_set donc mon idée était de stocker les bool_set déjà créé + celui qui est actuellement créé comme ça:

template <typename... Created, bool... static_val> 

mais le compilateur (g ++ 5.4 avec -std = C++ 11) me dit parameter pack ‘Created’ must be at the end of the template parameter list et je le change avec l'autre, il dit la même chose ...

Est-ce que quelqu'un a une idée? En fin de compte, je voudrais appeler ma fonction comme ça (ou équivalent): (d'une fonction)

generate_the_do_something<>::call({cond1, cond2}, {cond3, cond4, cond5}); 

Chaque liste initialiseur est un bool_set et ce generate_the_do_something est une structure qui permettra de créer le bool_set et appelez le do_something() avec eux.

Répondre

1

Si vous acceptez de séparer l'ensemble de celui-ci est relativement facile bool s avec un séparateur spécifique, comme dans l'exemple suivant

call_do_something(false, true, end_set{}, false, false, true, end_set{}); 

en utilisant std::tuple (pour emballer le bool_set terminé).

Voir le travail (au moins ... la compilation) exemple

#include <tuple> 

template<bool...> 
struct bool_set{}; 

struct end_set{}; 

template <bool...> 
struct My_obj{}; 

template <typename, typename> 
struct do_something; 

template <bool... bs1, bool... bs2> 
struct do_something<bool_set<bs1...>, bool_set<bs2...>> 
{ 
    static void func() 
    { 
     My_obj<bs1...> mo1; 
     My_obj<bs2...> mo2; 

     (void)mo1; // just to avoid a lot warnings 
     (void)mo2; // just to avoid a lot warnings 

     // do something else 
    } 
}; 

template <typename, typename> 
struct gtds; // ex generate_the_do_something 

template <typename ... Ts, bool ... Bs> 
struct gtds<std::tuple<Ts...>, bool_set<Bs...>> 
{ 
    template <typename ... As> 
    static void call (bool const & val, As const & ... as) 
    { 
     if (val) 
     gtds<std::tuple<Ts...>, bool_set<Bs..., true>>::call(as...); 
     else 
     gtds<std::tuple<Ts...>, bool_set<Bs..., false>>::call(as...); 
    } 

    template <typename ... As> 
    static void call (end_set const &, As const & ... as) 
    { gtds<std::tuple<Ts..., bool_set<Bs...>>, bool_set<>>::call(as...); } 

    template <bool bsEmpty = (sizeof...(Bs) == 0U)> 
    static typename std::enable_if< ! bsEmpty >::type call() 
    { do_something<Ts..., bool_set<Bs...>>::func(); } 

    template <bool bsEmpty = (sizeof...(Bs) == 0U)> 
    static typename std::enable_if<bsEmpty>::type call() 
    { do_something<Ts...>::func(); } 
}; 

template <typename ... Ts> 
void call_do_something (Ts const & ... ts) 
{ gtds<std::tuple<>, bool_set<>>::call(ts...); } 


int main() 
{ 
    call_do_something(false, true, end_set{}, false, false, true); 
    call_do_something(false, true, end_set{}, false, false, true, end_set{}); 
} 

Observer l'utilisation de SFINAE (std::enable_if sur call() sans paramètres) pour permettre l'appel de call_do_something() avec ou sans mettre fin à end_set{} suivant.

+0

Merci beaucoup!J'ai réussi à le modifier pour ajouter des paramètres à la fonction "func" :) Je n'ai finalement pas besoin du 'std :: enable_if' mais c'est un bon exemple pour comprendre comment cela fonctionne et quand l'utiliser, merci encore :) – Viridya