IdentifiantMot de passe
Loading...
Mot de passe oublié ?Je m'inscris ! (gratuit)
Navigation

Inscrivez-vous gratuitement
pour pouvoir participer, suivre les réponses en temps réel, voter pour les messages, poser vos propres questions et recevoir la newsletter

Langage C++ Discussion :

Revoila les template template !


Sujet :

Langage C++

  1. #1
    Membre émérite
    Profil pro
    Inscrit en
    Novembre 2004
    Messages
    2 764
    Détails du profil
    Informations personnelles :
    Localisation : France

    Informations forums :
    Inscription : Novembre 2004
    Messages : 2 764
    Points : 2 705
    Points
    2 705
    Par défaut Revoila les template template !
    Hello,

    J'ai l'occasion de me repencher sur les paramètres "template template" :

    J'aimerais faire un truc du genre :

    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    template <typename U, template <typename U> class T>
    struct RealType
    {
    	typedef typename T<U>::inner_index_type type;
    };
     
    template <typename U, typename T>
    struct RealType
    {
    	typedef T type;
    };
    Vous devez saisir l'idée.
    C'est assez basique, dans le domaine, mais je suis assez rouillé, et j'aimerais bien boucler ça avant ce soir.

    Vote aide serait la bienvenue pendant que je continue à chercher sur Google !

    (la recherche de chaines de groupes de mots sur le forum, ce n'est pas top)

  2. #2
    Membre expert
    Homme Profil pro
    Ingénieur développement logiciels
    Inscrit en
    Juin 2011
    Messages
    739
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Localisation : France, Hérault (Languedoc Roussillon)

    Informations professionnelles :
    Activité : Ingénieur développement logiciels

    Informations forums :
    Inscription : Juin 2011
    Messages : 739
    Points : 3 627
    Points
    3 627
    Par défaut
    Ceci ?

    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    template <typename U, typename T>
    struct RealType
    {
      typedef T type;
    };
     
    template <typename U, typename Z, template <typename> class T>
    struct RealType<U, T<Z> >
    {
      typedef typename T<U>::inner_index_type type;
    };

  3. #3
    Membre émérite
    Profil pro
    Inscrit en
    Novembre 2004
    Messages
    2 764
    Détails du profil
    Informations personnelles :
    Localisation : France

    Informations forums :
    Inscription : Novembre 2004
    Messages : 2 764
    Points : 2 705
    Points
    2 705
    Par défaut
    Merci !

    Ce qui me manquait, c'était ta ligne 8 : la configuration des paramètres template du trait.

    Du coup, j'ai trouvé plus simple, en éliminant le paramètre Z :

    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    template <typename U, typename T>
    struct RealType<U, T>
    {
        typedef T type;
    };
    
    template <typename U, template <typename> class T>
    struct RealType<U, T<U>>
    {
        typedef typename T<U>::inner_index_type type;
    };
    Mais je ne comprend pas pourquoi le compilateur n'accepte pas ce qui est en rouge.

    Je vais essayer maintenant de faire en sorte que dans la seconde fonction, U ne puisse être qu'un type bien précis.

  4. #4
    Membre expérimenté Avatar de Trademark
    Profil pro
    Inscrit en
    Février 2009
    Messages
    762
    Détails du profil
    Informations personnelles :
    Localisation : France

    Informations forums :
    Inscription : Février 2009
    Messages : 762
    Points : 1 396
    Points
    1 396
    Par défaut
    Ce qui est en rouge est une spécialisation, mais même si tu as définis le type de base, admettons :

    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    template <typename U, typename T>
    struct RealType
    {
        typedef T type;
    };
    Est-ce que ça a un sens de spécialiser ? Et ça ne marcherait pas car la spécialisation ne spécialise rien, du coup il faut tout simplement l'enlever.

    EDIT : Comme j'ai compris le truc je pense qu'il faut faire :

    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    // Type de base.
    template <Typename T>
    struct RealType
    {
    	typedef T type;
    };
     
    // Unwrap et selectionne le inner type.
    template <typename U, template <typename> class T>
    struct RealType<T<U> >
    {
    	typedef typename T<U>::inner_index_type type;
    };
    Pour rappel, tu définis d'abord un type de base et puis tu spécialises comme tu veux après, tu utilises alors la déclaration template pour déclarer ce que tu veux comme types et puis tu les "agences" dans la spécialisation pour que ça corresponde au nombre de template du type de base.

  5. #5
    Membre expert
    Homme Profil pro
    Ingénieur développement logiciels
    Inscrit en
    Juin 2011
    Messages
    739
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Localisation : France, Hérault (Languedoc Roussillon)

    Informations professionnelles :
    Activité : Ingénieur développement logiciels

    Informations forums :
    Inscription : Juin 2011
    Messages : 739
    Points : 3 627
    Points
    3 627
    Par défaut
    Je ne connais pas la finalité de RealType mais une approche différente est d'utiliser inner_index_type s'il existe dans T et dans le cas contraire utiliser T.

    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    template <typename T, bool = has_inner_index_type<T>::value>
    struct RealType
    {
    	typedef T type;
    };
     
    template <typename T>
    struct RealType<T, true>
    {
    	typedef typename T::inner_index_type type;
    };
    Avec has_inner_index_type qui peut ressembler à:
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    template<typename T>
    class has_inner_index_type
    {
      typedef char yes_type;
      typedef struct { char a[2]; } no_type;
     
      template<typename U>
      static yes_type test(U*, typename U::inner_index_type* = 0);
      static no_type test(...);
     
    public:
      static const bool value = sizeof(test(static_cast<T*>(0))) == sizeof(yes_type);
    };
    À voir si c'est pertinent.

  6. #6
    Membre expérimenté Avatar de Trademark
    Profil pro
    Inscrit en
    Février 2009
    Messages
    762
    Détails du profil
    Informations personnelles :
    Localisation : France

    Informations forums :
    Inscription : Février 2009
    Messages : 762
    Points : 1 396
    Points
    1 396
    Par défaut
    @jo_link_noir : C'est toujours intéressant d'apprendre des nouvelles techniques Par contre, il me semble que cette technique est principalement utilisée pour savoir si une classe hérite d'une autre – là où une spécialisation ne fonctionnera pas. Dans ce cas-ci je ne vois pas ce que ça ajoute à part une indirection (assez complexe en plus) au problème.

  7. #7
    Membre expert
    Homme Profil pro
    Ingénieur développement logiciels
    Inscrit en
    Juin 2011
    Messages
    739
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Localisation : France, Hérault (Languedoc Roussillon)

    Informations professionnelles :
    Activité : Ingénieur développement logiciels

    Informations forums :
    Inscription : Juin 2011
    Messages : 739
    Points : 3 627
    Points
    3 627
    Par défaut
    @Trademark: C'est aussi utilisé pour ça, la seul chose qui change est la fonction test. Comme il y a peu de changement, on peux facilement faire une macro pour générer la classe. Comme celle de Boost.MPL introspection.
    On peux aussi éliminer les indirection en utilisant typeof ou decltype.

    Juste pour exemple
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    6
    7
    8
    9
    template <typename T>
    class RealType
    {
      template<typename U>
      static typename U::inner_index_type test(U*);
      static T test(...);
    public:
      using type = decltype(test(static_cast<T*>(0)));
    };
    J'ai proposé ça car RealType me donne l'impression de vouloir connaître le type interne d'une classe. Mais je voit 2 problèmes:
    - Il faut que la classe soit obligatoire un template avec 1 seul paramètre pour que la spécification utilise inner_index_type.
    - Même si la spécification passe, la classe ne contient pas forcement un type inner_index_type.

    Ne connaissant pas le contexte, je lâcher une solution alternative ou complémentaire au second problème ^^.

  8. #8
    Membre émérite
    Profil pro
    Inscrit en
    Novembre 2004
    Messages
    2 764
    Détails du profil
    Informations personnelles :
    Localisation : France

    Informations forums :
    Inscription : Novembre 2004
    Messages : 2 764
    Points : 2 705
    Points
    2 705
    Par défaut
    Je vous remercie tous pour votre participation.

    Alors puisque le besoin s'en fait sentir, je vais préciser le contexte.

    Disons que j'ai deux vecteurs :

    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    std::vector<A> monVecteurDeA;
    std::vector<B> monVecteurDeB;
    Et deux autres vecteurs, servant à stocker des indices vers les deux vecteurs précédemment évoqués :

    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    std::vector<size_type> monVecteurDIndiceDansA;
    std::vector<size_type> monVecteurDIndiceDansB;
    Les types des éléments sont identiques. Et ça, ça me pose un problème.
    Je ne veux pas m'embrouiller sur la cible des indices (et dans le code confus dans lequel je travaille, ça peut arriver). Je veux blinder leur typage.

    Je définis donc un trait que je spécialise pour chaque type, car après tout, si je sais que ma 1ère série de vecteurs contient peu d'éléments, je ne vois pas pourquoi les éléments du second vecteur devraient avoir le vorace type size_t. Je précise que je connais le nombre d'instanciations maximal de A.

    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    template <typename T>
    struct IndexType
    {
    	typedef size_t inner_index_type;
    };
     
    template <>
    struct IndexType<A>
    {
    	typedef uint8_t inner_index_type;
    };
     
    template <>
    struct IndexType<B>
    {
    	typedef uint32_t inner_index_type;
    };
    Ensuite, j'ai des fonctions/classes qui ont besoin d'être templatisées par le type SCALAIRE des indices (dépouillés de leur wrapper, donc).

    J'en suis donc arrivé au code suivant (sans spécialisation des IndexType), en m'inspirant de la solution de trademark, qui me semblait mieux correspondre à mon besoin :

    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    template <typename T>
    struct IndexType
    {
    	typedef size_t inner_index_type;
    };
     
    template <typename T>
    struct RealType
    {
    	static_assert(std::is_integral<T>::value, "Must be an integral value");
    	typedef T type;
    };
     
    template <typename U>
    struct RealType<IndexType<U> >
    {
    	typedef typename RealType<typename IndexType<U>::inner_index_type>::type type;
    };
     
    RealType<IndexType<unsigned int>>::type essai_1;
    RealType<unsigned int>::type essai_2;
    Vous noterez que ma version spécialisée de RealType fait appel à la version générique, afin de pouvoir bénéficier de la vérification de la nature scalaire du "vrai type".

    J'aimerais au final bien avoir dans mes classes quelque chose comme :

    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    6
    struct A
    {
            ...
            typedef IndexType<A> index_type;
            ...
    };
    Cela rendrait l'utilisation plus naturelle (je rappelle que je connais le nombre d'instanciations maximal de A, et que ça a donc du sens que cela soit dans la classe A, et non dans des conteneurs de A). Mais je crois que cela ne me sera guère possible, pour cause d'inclusions circulaires...

    EDIT : quoique non, si je définis la spécialisation IndexType<A> dans le même fichier que la classe A

  9. #9
    Membre expérimenté Avatar de Trademark
    Profil pro
    Inscrit en
    Février 2009
    Messages
    762
    Détails du profil
    Informations personnelles :
    Localisation : France

    Informations forums :
    Inscription : Février 2009
    Messages : 762
    Points : 1 396
    Points
    1 396
    Par défaut
    Si tu veux définir le type d'index dans la classe, fait le directement :

    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    struct A
    {
      typedef uint8_t index_type;
    };
    Et après tu utilises IndexType comme on utiliserait iterator_traits sur un itérateur de vecteur. La différence avec iterator_traits c'est que tu veux utiliser une valeur par défaut si le type T n'offre pas d'index_type. Ce que j'ai appris aujourd'hui, c'est qu'on peut facilement le savoir avec la solution de jo_link_noir :

    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    28
    template<typename T>
    class has_index_type
    {
      typedef char yes_type;
      typedef struct { char a[2]; } no_type;
     
      // Overload de test, si U ne contient pas d'index_type alors SFINAE.
      template<typename U>
      static yes_type test(U*, typename U::index_type* = 0);
      // Et l'ellipse (...) accepte tout ! (Faut déjà savoir qu'on peut l'utiliser dans les fonctions mais bon.
      static no_type test(...);
     
    public:
      static const bool value = sizeof(test(static_cast<T*>(0))) == sizeof(yes_type); // Et finalement on test la taille du type de retour de test qui est différent suivant les 2 overloads.
    };
     
    template <typename T, bool = has_index_type<T>::value>
    struct RealType
    {
    	static_assert(std::is_integral<T>::value, "Must be an integral value");
    	typedef T type;
    };
     
    template <typename T>
    struct RealType<T, true>
    {
    	typedef typename RealType<typename T::index_type>::type type;
    };
    Et voila le travail ! Par défaut un size_t et si la structure offre un index_type alors on l'utilise

  10. #10
    Membre émérite
    Profil pro
    Inscrit en
    Novembre 2004
    Messages
    2 764
    Détails du profil
    Informations personnelles :
    Localisation : France

    Informations forums :
    Inscription : Novembre 2004
    Messages : 2 764
    Points : 2 705
    Points
    2 705
    Par défaut
    La solution ne me convient pas :

    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    A::index_type index_A;
    B::index_type index_B;
     
    bool flag = index_A < index_B;
    Or, je ne veux qu'on puisse faire ça.

    Ou alors, il faut le faire sciemment en demandant encore un sous-type :

    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    bool flag = index_A::inner_index_type < index_B::inner_index_type;
    Par ailleurs, la solution de jo_link_noir est à l'image du code de Boost : intellectuellement intéressante, mais peu lisible.

  11. #11
    Membre expérimenté Avatar de Trademark
    Profil pro
    Inscrit en
    Février 2009
    Messages
    762
    Détails du profil
    Informations personnelles :
    Localisation : France

    Informations forums :
    Inscription : Février 2009
    Messages : 762
    Points : 1 396
    Points
    1 396
    Par défaut
    Ha ok, je comprend maintenant l'idée du typage "fort". Par contre dans tous les cas, tes variables entières (obligatoirement unwrappé, à moins que tu ne fournisses des opérateurs +, - et tous le tralala dans IndexType?) pourront toujours être comparées entre elles, par exemple si tu déclares deux compteurs (ou que sais-je) :

    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    index_A::inner_index_type i;
    index_B::inner_index_type j;
    En fait, je ne vois pas comment tu feras pour faire respecter ce typage.

  12. #12
    Membre émérite
    Profil pro
    Inscrit en
    Novembre 2004
    Messages
    2 764
    Détails du profil
    Informations personnelles :
    Localisation : France

    Informations forums :
    Inscription : Novembre 2004
    Messages : 2 764
    Points : 2 705
    Points
    2 705
    Par défaut
    L'utilisateur n'est pas censé utiliser les inner_types, ou alors de manière exceptionnelle.

    Et oui, j'ai implémenté les opérateurs de comparaison.

    Par ailleurs, l'utilisateur, ça sera surtout moi.

    Le but est de ne pas, par inadvertance, mélanger dans une opération des indices pointant sur des données différentes. Mais si on insiste, il est toujours possible de le faire.

    Accessoirement, l'EDI permettra, par un survol de la variable, de voir son type, et donc le type de classe auquel se réfère l'indice.

  13. #13
    Membre expérimenté Avatar de Trademark
    Profil pro
    Inscrit en
    Février 2009
    Messages
    762
    Détails du profil
    Informations personnelles :
    Localisation : France

    Informations forums :
    Inscription : Février 2009
    Messages : 762
    Points : 1 396
    Points
    1 396
    Par défaut
    Vive l'ajout des typedef opaque dans le standard... (http://www.open-std.org/jtc1/sc22/wg...2013/n3515.pdf)

  14. #14
    Membre émérite
    Profil pro
    Inscrit en
    Novembre 2004
    Messages
    2 764
    Détails du profil
    Informations personnelles :
    Localisation : France

    Informations forums :
    Inscription : Novembre 2004
    Messages : 2 764
    Points : 2 705
    Points
    2 705
    Par défaut
    Oui !

    Je ne comprends pas pourquoi ils n'ont pas fait ça en même temps que les enum class. C'est dans le même esprit.

  15. #15
    Membre chevronné
    Avatar de Joel F
    Homme Profil pro
    Chercheur en informatique
    Inscrit en
    Septembre 2002
    Messages
    918
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Âge : 43
    Localisation : France, Essonne (Île de France)

    Informations professionnelles :
    Activité : Chercheur en informatique
    Secteur : Service public

    Informations forums :
    Inscription : Septembre 2002
    Messages : 918
    Points : 1 921
    Points
    1 921
    Par défaut
    Citation Envoyé par jo_link_noir Voir le message
    Je ne connais pas la finalité de RealType mais une approche différente est d'utiliser inner_index_type s'il existe dans T et dans le cas contraire utiliser T.
    Comme les specialisations tempaltes donnent lieu a des substitutions, une simple couche de SFINAE suffit.

    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
     
    template<class T, class R=void>
    struct enable_if_type
    {
      typedef R type;
    };
     
    template<class T, class Enable=void>
    struct RealType
    {
      typedef T type;
    };
     
    template<class T>
    struct  RealType< T
                , typename enable_if_type<typename T::inner_type>::type
                >
    {
      typedef typename T::inner_type type;
    };
    beaucoup plus simple et moins gourmand en compile-time

Discussions similaires

  1. Problème avec template template template
    Par oodini dans le forum Langage
    Réponses: 6
    Dernier message: 23/11/2012, 14h40
  2. Réponses: 2
    Dernier message: 10/01/2009, 13h38
  3. Comment déployer les List Template sous WSS 3.0
    Par nassim12 dans le forum SharePoint
    Réponses: 2
    Dernier message: 23/06/2008, 12h36
  4. Ou mettre les fichiers templates / langues
    Par slasch dans le forum Zend Framework
    Réponses: 2
    Dernier message: 29/11/2007, 00h01

Partager

Partager
  • Envoyer la discussion sur Viadeo
  • Envoyer la discussion sur Twitter
  • Envoyer la discussion sur Google
  • Envoyer la discussion sur Facebook
  • Envoyer la discussion sur Digg
  • Envoyer la discussion sur Delicious
  • Envoyer la discussion sur MySpace
  • Envoyer la discussion sur Yahoo