Spécialisation statique Métaprogrammation générique (1/2) * Repose sur plusieurs mécanismes * Généricité / patrons de composants * En C++: les templates * Instanciation des patrons sans surcoût * Au plus proche d'un code dédié * En C++: chaque instanciation d'un générique ==> code dédié * En Java: mécanisme de "type erasure" ==> pas de code dédié * Spécialisation "statique" * Possibilité de spécialisation d'un composant générique * Basée sur le type (ou la valeur statique) des paramètres génériques Métaprogrammation générique (2/2) * Objectifs * Composants génériques (au sens large) * Sans ou avec peu de baisse de performance * Le génie logiciel nous apprend que généralement * La généricité (au sens large) a un coût * Recherche d'un compromis entre généricité et efficacité * Mais avec la programmation générique * Beaucoup de travail à la compilation ==> peu de pertes Spécialisation statique * Composant générique = modèle indépendant des types * Mais cela peut être pénalisant * Exemple: recherche d'un élément dans une structure * Approches différentes suivant que la structure soit triée ou non ==> Mécanisme de spécialisation "statique" * Spécialisation du modèle générique pour un jeu de paramètres * Jeu de paramètres partiel ou complet * On parle aussi d'"instanciation" partielle ou complète * Associé au polymorphisme statique de l'instanciation * "Meilleure" instanciation choisie en fonction du jeu de paramètres Spécialisation d'une fonction générique * Modèle générique d'une fonction de calcul de moyenne template <int N> double moyenne(int * tab) { double somme = 0; for (int i = 0; i < N; ++i) somme += tab[i]; return (somme/N); } * Spécialisation du modèle pour N = 2 et N = 1 template <> double moyenne<2>(int * tab) { return (double(tab[0] + tab[1])/2); } template <> double moyenne<1>(int * tab) { return double(tab[0]); } * Attention à l'ordre * Déclarer d'abord la version générique, puis les versions spécifiques * En C++, spécialisation "partielle" d'une fonction (ou méthode) interdite Spécialisation d'une classe générique * Modèle générique d'un vecteur d'éléments template <typename T> class Vecteur { protected: T * elements; protected: int taille; ... public: T operator [] (int i) { return elements[i]; } }; * Spécialisation du modèle pour T = bool template <> class Vecteur<bool> { protected: char * elements; protected: int taille; ... public: bool operator [] (int i) { return ((elements[i/8] >> (i%8)) & 1); } }; Polymorphisme statique * Mécanisme statique lors de l'instanciation d'un modèle * Sélection de la version la plus spécialisée * En fonction du jeu de paramètres ==> Génération du code le plus dédié possible * Exemples d'instanciations * moyenne<10>(tab); ==> version générique * moyenne<2>(tab); ==> version spécialisée pour N = 2 * Vecteur<int> v; ==> version générique * Vecteur<bool> v; ==> version spécialisée pour T = bool Spécialisation partielle (1/2) * Spécialisation partielle = spécialisation avec un jeu de paramètres incomplet * Retour sur l'exemple de calcul de moyenne template <typename T,int N> class Moyenne { public: static T calculer(T * tab) { T somme = T(); for (int i = 0; i < N; ++i) somme += tab[i]; return (somme/T(N)); } }; * Spécialisation pour N = 2 (T reste inconnu) template <typename T> class Moyenne<T,2> { public: static T calculer(T * tab) { return ((tab[0] + tab[1])/T(2)); } }; Spécialisation partielle (2/2) * Exemple de recherche d'un élément dans un conteneur template <typename T,typename C> class Recherche { public: static bool executer(const C & conteneur, const T & element); }; * Spécialisation pour C = vector<T> (T reste inconnu) template <typename T> class Recherche< T,vector<T> > { public: static bool executer(const vector<T> & conteneur, const T & element); }; Alternative à l'héritage (1/2) * Polymorphisme dynamique ==> coût important à l'exécution * Exemple: stratégie de recherche dans un conteneur template <typename T> class Vecteur { ... public: virtual bool rechercher(const T & x); }; template <typename T> class VecteurTrie : public Vecteur<T> { ... public: bool rechercher(const T & x); }; * Héritage des conteneurs vraiment nécessaire ? * Aspect dynamique sans intérêt ==> spécialisation statique Alternative à l'héritage (2/2) * Version générique avec spécialisation statique template <typename T,typename C> class Recherche { public: static bool executer(const C & c, const T & e); }; template <typename T> class Recherche< T,VecteurTrie<T> > { public: static bool executer(const VecteurTrie<T> & c, const T & e); }; * Astuce: utiliser la déduction automatique des paramètres template <typename T,typename C> int rechercher(const C & c,const T & e) { return Recherche<T,C>::executer(c,e); } |