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 :

boost + gcc = bug sur les tests d'existence de fonction libre.


Sujet :

C++

  1. #1
    Membre actif

    Profil pro
    Inscrit en
    Avril 2010
    Messages
    356
    Détails du profil
    Informations personnelles :
    Localisation : France

    Informations forums :
    Inscription : Avril 2010
    Messages : 356
    Points : 206
    Points
    206
    Par défaut boost + gcc = bug sur les tests d'existence de fonction libre.
    Bonjour à tous,

    J'utilise boost 1.57 pour tester l'existence d'un opérateur << avec mingw 4.7 et je remarque un comportement plutôt perturbant avec le code ci-dessous.

    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
    #include <boost/type_traits/has_operator.hpp>
    #include <ostream>
    #include <iostream>
     
    #define BUG
     
    class Test
    {
     
    };
     
    #ifdef BUG
    bool debBoost = boost::has_left_shift<std::ostream&, const Test&, std::ostream&>::value;
    #endif
     
    std::ostream& operator<<(std::ostream&, const Test&);
     
    bool endBoost = boost::has_left_shift<std::ostream&,const Test&, std::ostream&>::value;
     
    int main()
    {
        #ifdef BUG
        std::cout<<"debBoost = "<<debBoost<<"\n";
        #endif
        std::cout<<"endBoost = "<<endBoost<<"\n";
    }
    Globalement, si j'active BUG, debBoost = endBoost = 0, sinon, endBoost =1. Est-ce un comportement normal ?

    Ensuite, j'ai voulu tester une solution maison (juste pour ce cas). La voici :

    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
    #include <ostream>
    #include <iostream>
    #include <type_traits>
     
    class Test
    {
     
    };
     
    struct delay
    {
        template<typename T>
        delay(const T&);
    };
     
    std::false_type operator<<(std::ostream&, delay);
     
    bool deb = !std::is_same<decltype(std::declval<std::ostream&>() << std::declval<const Test&>()), std::false_type>::value;
     
    std::ostream& operator<<(std::ostream&, const Test&);
     
    bool end = !std::is_same<decltype(std::declval<std::ostream&>() << std::declval<const Test&>()), std::false_type>::value;
     
    int main()
    {
        std::cout<<"deb = "<<deb<<"\n";
        std::cout<<"end = "<<end<<"\n";
    }
    Et je n'ai pas eu de soucis...

    Ce problème est-il uniquement chez moi ?

    Finalement, après un troisième test, le problème ne vient pas de boost puisque j'obtiens les même résultats que boost.

    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
    29
    30
    31
    32
    33
    34
    35
    36
    37
    38
    39
    #include <ostream>
    #include <iostream>
    #include <type_traits>
     
    #define BUG
    class Test
    {
     
    };
     
    struct delay
    {
        template<typename T>
        delay(const T&);
    };
     
    std::false_type operator<<(std::ostream&, delay);
     
    template<typename T>
    struct Has
    {
        static constexpr bool value = !std::is_same<decltype(std::declval<std::ostream&>() << std::declval<const T&>()), std::false_type>::value;
    };
     
    #ifdef BUG
    bool deb = Has<Test>::value;
    #endif
     
    std::ostream& operator<<(std::ostream&, const Test&);
     
    bool end = Has<Test>::value;
     
    int main()
    {
        #ifdef BUG
        std::cout<<"deb = "<<deb<<"\n";
        #endif
        std::cout<<"end = "<<end<<"\n";
    }
    Je suppose que tout ce ci est normal... Est-il possible de faire pour que la valeur soit évaluée lorsqu'elle est demandée et non à la première demande ?



    [EDIT] Je n'ai testé le deuxième code qu'après avoir commencé à éditer le message, et je ne pensais pas que le problème venait de boost (je pensais que ma solution allait échouer elle aussi). Quelqu'un pourrait-il déplacer la conversation dans le forum dédié à boost ?

    [EDIT2] Finalement, ce n'est pas lié à boost

  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
    a priori, c'est parce que tu instancies une template, puis la réinvoque.

    à la première instance, il trouve faux, du coup, à la seconde invocation, c'est toujours faux.
    Même si entre temps, la cause du faux a changé.
    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 actif

    Profil pro
    Inscrit en
    Avril 2010
    Messages
    356
    Détails du profil
    Informations personnelles :
    Localisation : France

    Informations forums :
    Inscription : Avril 2010
    Messages : 356
    Points : 206
    Points
    206
    Par défaut
    a priori, c'est parce que tu instancies une template, puis la réinvoque.

    à la première instance, il trouve faux, du coup, à la seconde invocation, c'est toujours faux.
    Même si entre temps, la cause du faux a changé.
    Oui, c'est ce que j'avais déduis du troisième test... Mais peut-on l'éviter ? Est-ce un comportement lié au compilateur utilisé ?

  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
    C'est lié à la nature même des templates.
    Une instance précise d'une template est matérialisée à sa première invocation.
    Concrètement, le code correspondant est "écrit et compilé" à ce moment-là.

    Une seconde invocation se contentera d'utiliser le même code.
    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 actif

    Profil pro
    Inscrit en
    Avril 2010
    Messages
    356
    Détails du profil
    Informations personnelles :
    Localisation : France

    Informations forums :
    Inscription : Avril 2010
    Messages : 356
    Points : 206
    Points
    206
    Par défaut
    Ok, mais est-ce garanti que les résultats soient les mêmes avec tous les compilateurs ?

    Notamment, si je fait un #define HAS_SHIFT_LEFT(C) !std::is-same... (voir deuxième solution), puis-je être certain que la valeur est recalculée à chaque fois ?
    Y a t-il une solution plus "propre" qu'un #define pour obtenir le même résultat ?

    [EDIT] Je vais profiter de ce thread pour demander s'il existe un trick pour contourner les problèmes de templates dans le test d'existence de fonction (j'imagine que non, mais on ne sait jamais...). Imaginons que j'ai template<typename T> auto serialize(const T&) mais que cette fonction ne compile pas pour toutes les classes. Est-il possible de définir un constexpr bool hasSerialize<C> ?

  6. #6
    Membre éprouvé
    Profil pro
    Ingénieur développement logiciels
    Inscrit en
    Mars 2009
    Messages
    552
    Détails du profil
    Informations personnelles :
    Localisation : France

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

    Informations forums :
    Inscription : Mars 2009
    Messages : 552
    Points : 1 060
    Points
    1 060
    Par défaut
    Citation Envoyé par NoIdea Voir le message
    [EDIT] Je vais profiter de ce thread pour demander s'il existe un trick pour contourner les problèmes de templates dans le test d'existence de fonction (j'imagine que non, mais on ne sait jamais...).
    J'ai vaguement l'impression que ma crainte face à ton précédent thread se confirme. Tu vas essayer de tout faire contrôler à ton compilateur (y compris ce qu'il contrôle déjà) à coup de template qui ne feront qu'une chose : Complexifier ton code.

    Imaginons que j'ai template<typename T> auto serialize(const T&) mais que cette fonction ne compile pas pour toutes les classes
    Ben tu laisses le compilateur afficher un message d'erreur... Un utilisateur de ton système pourra toujours faire une adaptation pour que ça deviennent possible sur sa classe.

    Tu comptes mettre des genres de "concept" sur chaque erreur de programmation possible quand bien même le compilateur effectue déjà un contrôle?

  7. #7
    Membre actif

    Profil pro
    Inscrit en
    Avril 2010
    Messages
    356
    Détails du profil
    Informations personnelles :
    Localisation : France

    Informations forums :
    Inscription : Avril 2010
    Messages : 356
    Points : 206
    Points
    206
    Par défaut
    J'ai vaguement l'impression que ma crainte face à ton précédent thread se confirme. Tu vas essayer de tout faire contrôler à ton compilateur (y compris ce qu'il contrôle déjà) à coup de template qui ne feront qu'une chose : Complexifier ton code.
    Possible... L'idée, c'est surtout de cacher à l'utilisateur ce qu'il se passe à l’intérieur des fonctions (mais il garde accès à toute la spécification via la documentation).

    Pour mieux comprendre ce qu'il se passe, imaginons qu'on écrive quelque chose du genre (pseudo code)

    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>
    struct Serialize
    {
      static constexpr bool value = hasBoostSerializeFunction || (hasOperator ostream& << (ostream&, const C&) && hasOperator istream& >> (istream&, C&));
      /*Vérifie que serialize ou (<< et >>) vérifie la partie "dynamique" de serialize ou (<< et >>). 
         Bon en gros, que load(save()) = Identity (quand pas d'exception et ...). 
         Cette fonction ne sert qu'au debug. Et fait un lot d'assert par exemple.
      */
      void check();
     
      auto save(ostream&, const C&);
      C load(istream&);
    };
    L'idée c'est que save et load vont faire les static_assert(value, "..."); permettant d'éviter des erreurs incompréhensibles à la compilation. Et que check va pouvoir faire les tests nécessaires sur le type C pour que lorsqu'il y a une erreur à l’exécution, on puisse trouver l'erreur.

    L'utilisateur est alors protégé par la bibliothèque d'erreurs incompréhensibles (compilation ou exécution) et il n'a pas à rajouter du boilerplate code juste parce qu'il a << et >> au lieu de serialize par exemple.

    Honnêtement, si on pouvait avoir sur chaque fonction template un static_assert qui soit réussi et la fonction compile, soit fail et explique pourquoi, ce serait tellement un plus au c++ (en gros un concept quoi...). Aussi, un peu de réflexion pour faire des fonctions comme serialize, operator==, ... automatiques, ce ne serait pas de trop (mais il me semble que c'est prévu).

    Aussi, pendant que j'y suis, quelqu'un sait-il pourquoi le mot clé const a été inventé mais on a pas continué sur la voie de la vérification statique en C++ ?

  8. #8
    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
    Parce que const est beaucoup plus ancien que les templates, etc.

    Il permet justement au compilateur de faire énormément de validation et des quantités astronomiques d'optimisations.
    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

Discussions similaires

  1. Tutoriel sur les tests, bugs & rock'n'roll
    Par Francis Walter dans le forum Architecture
    Réponses: 2
    Dernier message: 26/10/2014, 02h28
  2. Information sur les tests réseaux
    Par KasTelo dans le forum Hardware
    Réponses: 6
    Dernier message: 28/07/2006, 19h46
  3. [VBS]Bug sur les boucles for.
    Par méphistopheles dans le forum VBScript
    Réponses: 7
    Dernier message: 24/03/2006, 22h17
  4. Réponses: 4
    Dernier message: 25/04/2005, 15h48

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