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

C++ Discussion :

typedef ou using à la place d'une macro


Sujet :

C++

  1. #1
    Membre éprouvé
    Inscrit en
    Avril 2005
    Messages
    1 110
    Détails du profil
    Informations forums :
    Inscription : Avril 2005
    Messages : 1 110
    Points : 937
    Points
    937
    Par défaut typedef ou using à la place d'une macro
    Bonjour

    J'ai défini une classe dont l'instantiation est assez verbeuse.
    Pour faciliter son usage et sa lisibilité j'utilise une macro, mais je souhaiterais plutôt utiliser un typedef ou un using.
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    6
    7
    8
    9
    template<class C, typename T, T C::*P>
    struct my_trait
    {
    	typedef C class_type;
    	typedef T member_type;
    	static constexpr T C::* offset = P;
    };
     
    #define MY_TRAIT(table, field) my_trait<table, decltype(table::field), &table::field>
    Exemple:
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    struct my_class
    {
    std::string m_Name;
    //...
    };
     
     
    ...
    typename my_trait<my_class, decltype(my_class::m_Name), &my_class::m_Name>::member_type name;//un peu lourd
     
    ...
    typename MY_TRAIT(my_class, m_Name)::member_type name;//mieux, mais c'est une macro
    L'usage de la macro ci-dessus est nettement plus lisible, mais j'aimerais utiliser quelque chose comme ceci:
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    template<class C>
    using my_name = my_trait<C, decltype(C::m_Name), &C::m_Name>;//ok, mais comment "templatiser" m_Name ?
     
    template<class C, ??? M>
    using my_member = my_trait<C, decltype(C::M), &C::M>;//euh... comment ???
    ... et je ne trouve pas
    Merci pour votre aide.

  2. #2
    Expert éminent sénior

    Femme Profil pro
    Ingénieur développement logiciels
    Inscrit en
    Juin 2007
    Messages
    5 189
    Détails du profil
    Informations personnelles :
    Sexe : Femme
    Localisation : France, Essonne (Île de France)

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

    Informations forums :
    Inscription : Juin 2007
    Messages : 5 189
    Points : 17 141
    Points
    17 141
    Par défaut
    Peut-être avec un pointeur de membre, mais je ne pense pas que cela soit possible facilement.

    Quel est ton usage de la macro.

    Parce qu'a priori, tu devrait pouvoir utiliser la déduction de parametre template disponible sur une fonction, quitte à passer par decltype.

    a priori, tu veux extraire le type d'un membre d'une classe, à partir d'un pointeur de membre.

    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    6
    7
    //une fonction non définie, exactement dans l'idée de std::declval<T>()
    template <typename T, typename M>
    M member_type_of( M (*T::) );
     
    int f() {
        decltype( member_type_of(&table::field) ) & field_reference = table.field;
    }
    Et mieux, beaucoup mieux. Quitte à être en C++11, tu ne pourrais pas juste utiliser auto ou decltype(auto)?
    Mes principes de bases du codeur qui veut pouvoir dormir:
    • Une variable de moins est une source d'erreur en moins.
    • Un pointeur de moins est une montagne d'erreurs en moins.
    • Un copier-coller, ça doit se justifier... Deux, c'est un de trop.
    • jamais signifie "sauf si j'ai passé trois jours à prouver que je peux".
    • La plus sotte des questions est celle qu'on ne pose pas.
    Pour faire des graphes, essayez yEd.
    le ter nel est le titre porté par un de mes personnages de jeu de rôle

  3. #3
    Membre éprouvé
    Inscrit en
    Avril 2005
    Messages
    1 110
    Détails du profil
    Informations forums :
    Inscription : Avril 2005
    Messages : 1 110
    Points : 937
    Points
    937
    Par défaut
    Non, pas juste le type d'un membre. Mon exemple permet simplement d'illustrer la lourdeur d'écriture sans macro.

    En fait ma structure my_trait<> est un ... trait (elle contient aussi des fonctions membres static), elle est donc à priori utilisée comme paramètre de template.
    Exemple:
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    struct Employee
    {
    	std::string firstname;
    	std::string lastname;
     
    	//... etc
    };
     
    typedef order_by<
    	MY_TRAIT(Employee, lastname),
    	MY_TRAIT(Employee, firstname)
    > by_employee_name;
    Mon but est juste d'éviter une écriture lourde comme celle-ci:
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    typedef order_by<
    	my_trait<Employee, decltype(Employee::lastname), &Employee::lastname>,
    	my_trait<Employee, decltype(Employee::firstname), &Employee::firstname>
    > by_employee_name;
    order_by<> ressemble à ceci:
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    template<class ...M>
    struct order_by
    {
    	...
    };

  4. #4
    Expert éminent sénior

    Femme Profil pro
    Ingénieur développement logiciels
    Inscrit en
    Juin 2007
    Messages
    5 189
    Détails du profil
    Informations personnelles :
    Sexe : Femme
    Localisation : France, Essonne (Île de France)

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

    Informations forums :
    Inscription : Juin 2007
    Messages : 5 189
    Points : 17 141
    Points
    17 141
    Par défaut
    Ma solution donne ceci, ce qui est déjà mieux:
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    typedef order_by<
    	decltype(member_trait_of(&Employee::lastname)),
    	decltype(member_trait_of(&Employee::firstname))
    > by_employee_name;
    Attention tout de même, ton trait possède en paramètre template un pointeur de membre, je ne suis pas sure que cela soit correct (voire possible)

    A tout hasard, tu peux définir ton ordre d'une autre manière:
    Il s'agit de profiter de std::tie() et de l'opérateur de comparaison des std::tuple.
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    bool employee_order_name(Employee const& a, Employee const& b) {
        return std::tie(a.lastname, a.firstname) < std::tie(b.lastname, b.firstname);
    }
    Voire même, :
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    auto employee_order_func(Employee const& e) { return std::tie(e.lastname, e.firstname); }
    template <typename KeyMaker>
    struct employee_comparator {
    public:
        employee_comparator(KeyMaker f): f(f) {}
        bool operator(Employee const& a, Employee const& b) const { return f(a) < f(b);}
    private:
        KeyMaker f;
    };
    template <typename KeyMaker>
    employee_comparator<KeyMaker> make_employee_comparator(KeyMaker const& f) { return employee_comparator<KeyMaker>(f); }
     
    std::sort(employees.first(), employees.last(), make_employee_comparator(&employee_order_func));
    Et puis, tant qu'a faire, autant prendre directement l'outil adapté, la lambda:
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    std::sort(employees.first(), employees.last(),
        [](Employee const& a, Employee const& b) {return return std::tie(a.lastname, a.firstname) < std::tie(b.lastname, b.firstname);}
    );

    D'une manière générale, un nom de membre n'est pas une valeur. Il n'est pas stockable dans une variable (c'est le role que remplit un peu le pointeur de membre), et du coup, n'est pas utilisable comme parametre d'une template, ni d'une fonction.
    Mes principes de bases du codeur qui veut pouvoir dormir:
    • Une variable de moins est une source d'erreur en moins.
    • Un pointeur de moins est une montagne d'erreurs en moins.
    • Un copier-coller, ça doit se justifier... Deux, c'est un de trop.
    • jamais signifie "sauf si j'ai passé trois jours à prouver que je peux".
    • La plus sotte des questions est celle qu'on ne pose pas.
    Pour faire des graphes, essayez yEd.
    le ter nel est le titre porté par un de mes personnages de jeu de rôle

  5. #5
    Membre confirmé
    Avatar de gb_68
    Homme Profil pro
    Ingénieur développement logiciels
    Inscrit en
    Août 2006
    Messages
    232
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Âge : 40
    Localisation : France, Haut Rhin (Alsace)

    Informations professionnelles :
    Activité : Ingénieur développement logiciels
    Secteur : High Tech - Éditeur de logiciels

    Informations forums :
    Inscription : Août 2006
    Messages : 232
    Points : 546
    Points
    546
    Par défaut
    Il est possible de simplifier un peu l'écriture en supprimant un paramètre template :
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    template<typename T, T P>
    struct my_trait;
     
    template<typename C, typename T, T C::*P>
    struct my_trait<T C::*, P>
    {
    	typedef C class_type;
    	typedef T member_type;
    	static constexpr T C::* offset = P;
    };
    Usage :
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
     
    typedef order_by<
    	my_trait<decltype(&Employee::lastname), &Employee::lastname>,
    	my_trait<decltype(&Employee::firstname), &Employee::firstname>
    > by_employee_name;
    Mais pour éviter la répétition qui subsiste, il faut malheureusement attendre le C++17 qui permettra quelque chose comme ça :
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    template<auto P>
    struct my_trait_17 : my_trait<decltype(P), P>
    {};
    P0127r1
    P0127r2

  6. #6
    Membre éprouvé
    Inscrit en
    Avril 2005
    Messages
    1 110
    Détails du profil
    Informations forums :
    Inscription : Avril 2005
    Messages : 1 110
    Points : 937
    Points
    937
    Par défaut
    Extrait de #include <boost/multi_index/member.hpp>
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    template<class Class, typename Type, Type Class::*PtrToMember>//ici, tout à fait semblable à my_trait<>
    struct member
    {
      typedef Type result_type;
     
      ...
    };
     
    #define BOOST_MULTI_INDEX_MEMBER(Class,Type,MemberName) \
    ::boost::multi_index::member< Class,Type,&Class::MemberName >
    La déclaration des paramètres template dans mon code est valide, d'ailleurs il s'inspire directement de boost
    Y compris la macro, et la mienne est plus concise que celle de boost grace à l'utilisation de decltype (qui n'existait probablement pas lorsque boost a créé BOOST_MULTI_INDEX_MEMBER).

    (et puis, voir Template parameters and template arguments, et Pointer to member declarator)

    boost n'a plus remis à jour sa bibliothèque multi_index depuis un certain temps, je me disais qu'aujourd'hui l'usage de cette macro pouvait être évité en utilisant un using (ou autre) bien pensé.

    C'est tout ce que je souhaite pour l'instant.

    @ternel, merci pour ta réponse, mais order_by a besoin de tous les paramètres template de my_trait que j'ai précisé, pas uniquement le type du membre.
    Pour le reste, on sort du cadre de ma demande (qui est de remplacer une macro par "autre chose" de meilleure pratique à C++).
    Mais, pour info, j'ai écrit un bon gros foncteur générique qui m'évite désormais d'écrire du code comparateur, comme celui que tu proposes en exemple, ou comme la lambda (que je ne trouve pas mieux personnellement, je préfèrerais garder le code comparateur près de la définition de la structure plutôt que de l'éparpiller dans les algos std::sort, std::binary_search, std::lower_bound, etc, où le risque de copier-coller foireux est grand).
    J'ai des dizaines de structures, utilisant plusieurs relations d'ordre pour certaines. C'est tellement plus sécurisant d'écrire de simples typedef order_by<> comme celui donné en exemple que d'écrire des comparateurs complets (même utilisant std::tie<>).

    @gb_68, merci !
    Je suis ça de près

+ Répondre à la discussion
Cette discussion est résolue.

Discussions similaires

  1. Supprimer un objet / text via une macro placée sur un bouton
    Par Lauvira dans le forum VBA PowerPoint
    Réponses: 1
    Dernier message: 17/11/2012, 05h43
  2. Réponses: 2
    Dernier message: 21/01/2010, 17h02
  3. [VBA-E] [Excel] Lancer une macro à une heure donnée
    Par Lysis dans le forum Macros et VBA Excel
    Réponses: 2
    Dernier message: 16/10/2002, 12h15
  4. Qu'est-ce qu'une macro ?
    Par karli dans le forum Assembleur
    Réponses: 2
    Dernier message: 01/09/2002, 03h38
  5. Réponses: 2
    Dernier message: 22/07/2002, 12h13

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