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

Normalisation C++ Discussion :

Proposition à envoyer avant le meeting de prague


Sujet :

Normalisation C++

Vue hybride

Message précédent Message précédent   Message suivant Message suivant
  1. #1
    Expert éminent
    Avatar de koala01
    Homme Profil pro
    aucun
    Inscrit en
    Octobre 2004
    Messages
    11 644
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Âge : 53
    Localisation : Belgique

    Informations professionnelles :
    Activité : aucun

    Informations forums :
    Inscription : Octobre 2004
    Messages : 11 644
    Par défaut Proposition à envoyer avant le meeting de prague
    Salut,

    Si je viens vers vous aujourd'hui, c'est parce que j'ai remarqué quelques comportements que je juge complètement anormaux de la part du compilateur lorsque l'on utilise les constexpr.

    Je me suis donc directement adressé à la liste de diffusion du SG7 (le groupe "réflexion" du commité) pour expliquer mon point de vue.

    Cela n'a pas été sans peine, car j'ai du défendre mon point de vue, mais, au final (je vous passe les détails) voici la réponse que l'on m'a faite:
    So, are you saying unsigned integer overflow/underflow in constexpr
    computations should make the expression non-constant (cf. [expr.const])?

    That's probably a workable suggestion; please write a paper
    and submit it to the pre-Prague mailing.
    Seulement, sur ce coup là, je ne sais pas trop bien comment m'y prendre. Du coup, ayant terminé la rédaction de ce papier, j'aimerais avoir votre avis sur le sujet avant de l'envoyer vers les pontes

    Si vous pouviez donc trouver un peu de temps pour le lire (attention, il y a quatre pages, en anglais) et me donner votre avis, je vous en serais très reconnaissant

    Merci d'avance à tout le monde
    Images attachées Images attachées
    A méditer: La solution la plus simple est toujours la moins compliquée
    Ce qui se conçoit bien s'énonce clairement, et les mots pour le dire vous viennent aisément. Nicolas Boileau
    Compiler Gcc sous windows avec MinGW
    Coder efficacement en C++ : dans les bacs le 17 février 2014
    mon tout nouveau blog

  2. #2
    Expert confirmé
    Avatar de Luc Hermitte
    Homme Profil pro
    Développeur informatique
    Inscrit en
    Août 2003
    Messages
    5 296
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Localisation : France, Haute Garonne (Midi Pyrénées)

    Informations professionnelles :
    Activité : Développeur informatique
    Secteur : Aéronautique - Marine - Espace - Armement

    Informations forums :
    Inscription : Août 2003
    Messages : 5 296
    Par défaut
    Première lecture très très rapide, j'ai reviendrai plus tard.

    ---------------
    Je sais pas si tu as accès à un correcteur pour faire le pdf, j'ai noté ces typos

    - wether prend un h-> whether
    - forme -> form
    - occures -> occurs
    - Anlnowledgments -> Acknowledgments (je ne sais plus si ça prend un s d'habitude)
    ------------

    Sur le fond.
    La compatibilité avec le C n'est pas "irrelevant" (je ne sais plus parler français)...
    En effet, une expression au fond constante qui va apparaître dans une tierce expression telle qu'une boucle ou un test doit être traité de la même façon en tant que pseudo-rvalue que ce soit en C ou en C++, ou que si ça finit dans une pseudo-lvalue constant: les surprises ne sont pas acceptables. Le même comportement doit se retrouver dans tous les cas

    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
     
    while (i != UMAX + 1) // C ou C++
     
    constexpr auto M = UMAX +1;
    while(i != M)
    J'ai peur que le changement que tu proposes casse beaucoup de code.

    Oui, les promotions, c'est pourri. Ou le defined overflow c'est pourri, mais on ne peut pas y toucher sinon le traitement des expressions constantes deviendrait contextuel, et ce n'est pas acceptable pour raison du principe de moindre surprise.

    En revanche, si sur les overflows constants d'unsigned (les signed, c'est UB, donc en s'en fout), les compilateurs donnent des résultats différents (je pense que tu peux donner un lien godbolt pour illustrer en plus du code que tu recopies dans le papier), alors il y a un vrai problème qui doit être adressé. Il n'est pas normal que le cas defined ne le soit plus une fois passé chez les constantes.

    Aussi, toujours si l'overflow constant d'unsigned était UB (ce qui me surprend), alors ton papier peut passer car au fond, tu passes à defined ~~> error. De plus dans ce cas tu devrais signaler clairement dans le résumé que tu proposes de rendre defined un cas UB pour des raisons de moindre surprise et que la situation était déjà source de problèmes de portabilité.
    Blog|FAQ C++|FAQ fclc++|FAQ Comeau|FAQ C++lite|FAQ BS|Bons livres sur le C++
    Les MP ne sont pas une hotline. Je ne réponds à aucune question technique par le biais de ce média. Et de toutes façons, ma BAL sur dvpz est pleine...

  3. #3
    Expert éminent
    Avatar de koala01
    Homme Profil pro
    aucun
    Inscrit en
    Octobre 2004
    Messages
    11 644
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Âge : 53
    Localisation : Belgique

    Informations professionnelles :
    Activité : aucun

    Informations forums :
    Inscription : Octobre 2004
    Messages : 11 644
    Par défaut
    Citation Envoyé par Luc Hermitte Voir le message
    Première lecture très très rapide, j'ai reviendrai plus tard.

    ---------------
    Je sais pas si tu as accès à un correcteur pour faire le pdf, j'ai noté ces typos

    - wether prend un h-> whether
    - forme -> form
    - occures -> occurs
    - Anlnowledgments -> Acknowledgments (je ne sais plus si ça prend un s d'habitude)
    ------------
    Merci pour les typo
    Sur le fond.
    La compatibilité avec le C n'est pas "irrelevant" (je ne sais plus parler français)...
    En effet, une expression au fond constante qui va apparaître dans une tierce expression telle qu'une boucle ou un test doit être traité de la même façon en tant que pseudo-rvalue que ce soit en C ou en C++, ou que si ça finit dans une pseudo-lvalue constant: les surprises ne sont pas acceptables. Le même comportement doit se retrouver dans tous les cas
    Je comprend ton point de vue, et, au risque de te surprendre, je suis en partie d'accord avec toi: il ne faut pas casser le code ... runime

    Si ce n'est que, quand nous atteignons ce code, nous ne somme déjà plus dans le contexte qui nous occupe ici et qui est ... la définition d'une valeur constexpr.

    Si tu te place dans un contexte dans lequel tu défini une variable (i, en l'occurrence) rien ne s'oppose à la promotion / conversion de ta constexpr pour qu'elle s'adapte au type indiqué. (tu as d'ailleurs attiré mon attention sur une modification à apporter à la proposition de texte pour la norme, merci
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
     
    while (i != UMAX + 1) // C ou C++
     
    constexpr auto M = UMAX +1;
    while(i != M)
    i dans le code que tu présentes n'est absolument pas constexpr, que je sache. C'est UMAX qui l'est

    Quant à M, ben, désolé, mais c'est une valeur incompatible avec le type de UMAX, le compilateur n'a pas à accepter ce genre de chose.
    J'ai peur que le changement que tu proposes casse beaucoup de code.
    Oui, les promotions, c'est pourri. Ou le defined overflow c'est pourri, mais on ne peut pas y toucher sinon le traitement des expressions constantes deviendrait contextuel, et ce n'est pas acceptable pour raison du principe de moindre surprise.
    La définition d'une valeur contexpr est toujours contextuelle, au même titre que la définition d'une valeur énumérée...

    C'est, justement, en raison du principe de moindre surprise, qu'il faut donc s'assurer que le comportement du compilateur soit toujours identique en cas d'overflow/ underflow.
    En revanche, si sur les overflows constants d'unsigned (les signed, c'est UB, donc en s'en fout), les compilateurs donnent des résultats différents (je pense que tu peux donner un lien godbolt pour illustrer en plus du code que tu recopies dans le papier), alors il y a un vrai problème qui doit être adressé. Il n'est pas normal que le cas defined ne le soit plus une fois passé chez les constantes.
    Ben, justement, on parle de constante propres au compilateur, et, dans ce cas, un UB est impardonnable

    Aussi, toujours si l'overflow constant d'unsigned était UB (ce qui me surprend),
    C'est pourtant le cas...
    alors ton papier peut passer car au fond, tu passes à defined ~~> error. De plus dans ce cas tu devrais signaler clairement dans le résumé que tu proposes de rendre defined un cas UB pour des raisons de moindre surprise et que la situation était déjà source de problèmes de portabilité.
    Ah, de fait, je peux faire cela...

    Un grand merci pour ton retour
    A méditer: La solution la plus simple est toujours la moins compliquée
    Ce qui se conçoit bien s'énonce clairement, et les mots pour le dire vous viennent aisément. Nicolas Boileau
    Compiler Gcc sous windows avec MinGW
    Coder efficacement en C++ : dans les bacs le 17 février 2014
    mon tout nouveau blog

  4. #4
    Expert confirmé
    Avatar de Luc Hermitte
    Homme Profil pro
    Développeur informatique
    Inscrit en
    Août 2003
    Messages
    5 296
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Localisation : France, Haute Garonne (Midi Pyrénées)

    Informations professionnelles :
    Activité : Développeur informatique
    Secteur : Aéronautique - Marine - Espace - Armement

    Informations forums :
    Inscription : Août 2003
    Messages : 5 296
    Par défaut
    Je te répond en coup de vent.
    Il n'y a aucune différence entre while( i != UMAX+1) et constexpr auto M = UMAX+1. Dans les deux cas, il y a une sous-expression UMAX+1 qui est constante et qui va être résolue par le compilateur. Dans le second cas on la stocke explicitement, alors que dans le premier on consomme sur place.

    Cette sous-expression a un type, et toute ta question est: quel doit être le type de cette sous-expression?

    De fait, refuser UMAX+1 devrait être fait partout, y compris dans la boucle qui est runtime (ou pas, elle pourrait être dans une sous fonction constexpr).

    C'est ce point qui va être la source de tous les accrochements. La déclaration constexpr, ce qu'elle dit (si je ne me trompe pas), c'est que l'on exige que l'expression UMAX+1 soit constante, et on veut lui donner un nom. Sans cette étape si UMAX est constante, alors UMAX+1 l'est aussi. Même si on ne l'exige pas. Et donc les règles de résolution que tu proposes devraient s'appliquer à cet endroit là. Et donc, il y a des risques de refuser de compiler des codes non portables (si UB) qui compilaient.


    J'ai pensé à un truc après, pour les histoires de boucles infinies. Une boucle infinie sans effets de bords est un UB. Attention que dans ta démonstration, l'UB observé ne soit pas celui induit par une boucle infinie.
    Blog|FAQ C++|FAQ fclc++|FAQ Comeau|FAQ C++lite|FAQ BS|Bons livres sur le C++
    Les MP ne sont pas une hotline. Je ne réponds à aucune question technique par le biais de ce média. Et de toutes façons, ma BAL sur dvpz est pleine...

  5. #5
    Expert éminent
    Avatar de koala01
    Homme Profil pro
    aucun
    Inscrit en
    Octobre 2004
    Messages
    11 644
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Âge : 53
    Localisation : Belgique

    Informations professionnelles :
    Activité : aucun

    Informations forums :
    Inscription : Octobre 2004
    Messages : 11 644
    Par défaut
    Citation Envoyé par Luc Hermitte Voir le message
    Je te répond en coup de vent.
    Il n'y a aucune différence entre while( i != UMAX+1) et constexpr auto M = UMAX+1. Dans les deux cas, il y a une sous-expression UMAX+1 qui est constante et qui va être résolue par le compilateur. Dans le second cas on la stocke explicitement, alors que dans le premier on consomme sur place.
    Effectivement, on a toujours la même expression UMAX+1, et, toujours, dans le cadre d'une affectation.
    Mais le fait est que, avec la boucle while(i=UMAX+1), la donnée définie n'est -- a priori -- pas constante, sauf si l'on veut créer une boucle infinie(*), alors que avec l'expression constexpr auto overflow = UMAX+1;, nous définissons expressement une donnée nommée overflow comme devant être considérée comme une constante de compilation.

    Autrement dit, ce qui importe dans ces expressions, c'est bien le type (et surtout l'obligation de constance à laquelle il est soumis) de l'opérateur de gauche.

    (*)D'ailleurs, si i est non signé et de même type que UMAX, tu vas avoir un sérieux problème si ton objectif est de créer une boucle infinie, car UMAX +1 est sensé valoir 0 (en tout cas pour les unsigned int, ce qui est le seul comportement clairement défini au runtime). Or, 0 est classiquement évalué à ... false lorsqu'il est converti en booléen. Ta boucle ne sert donc à rien, vu qu'elle ne sera jamais exécutée.


    Cette sous-expression a un type, et toute ta question est: quel doit être le type de cette sous-expression?
    La réponse est simple : son type doit impérativement être celui de l'opérande qui se trouve à gauche de l'opérateur d'affectation.

    De fait, refuser UMAX+1 devrait être fait partout, y compris dans la boucle qui est runtime (ou pas, elle pourrait être dans une sous fonction constexpr).
    Ce serait effectivement le plus logique. Mais là, nous risquons effectivement de créer une incompatibilité majeure avec C et de casser pas mal de code

    C'est ce point qui va être la source de tous les accrochements. La déclaration constexpr, ce qu'elle dit (si je ne me trompe pas), c'est que l'on exige que l'expression UMAX+1 soit constante, et on veut lui donner un nom. Sans cette étape si UMAX est constante, alors UMAX+1 l'est aussi. Même si on ne l'exige pas. Et donc les règles de résolution que tu proposes devraient s'appliquer à cet endroit là. Et donc, il y a des risques de refuser de compiler des codes non portables (si UB) qui compilaient.
    Et c'est justement là tout le problème:
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    #include <type_traits>
    #include <limits>
    #include <iostream>
    #include <cstdint>
    using namespace std;
     
     
    constexpr uint32_t UI32Max = integral_constant<uint32_t, numeric_limits<uint32_t>::max()+1>::value;
     
    int main(){
        std::cout<<UI32Max::value<<"\n";
    }
    compile avec n'importe quel compilateur, et va afficher ... 0. Tout cela à cause d'une règle disant que UMAX+1 = 0 et qui ne devrait être applicable qu'au runtime, étant donné que, dans le cas présent, le développeur demande explicitement une donnée de type uint32_t. Aucune promotion / conversion ne devrait être faite lors de l'évaluation de l'expression uint32_t, numeric_limits<uint32_t>::max()+1 etant donné que le type principal de l'expression correspond en tout point au type de l'opérande de gauche.

    De mon point de vue, le compilateur devrait clairement dire au développeur (qui est sensé savoir ce qu'il fait) qu'il a choisi un type trop petit pour permettre la représentation de la valeur qu'il demande.
    A méditer: La solution la plus simple est toujours la moins compliquée
    Ce qui se conçoit bien s'énonce clairement, et les mots pour le dire vous viennent aisément. Nicolas Boileau
    Compiler Gcc sous windows avec MinGW
    Coder efficacement en C++ : dans les bacs le 17 février 2014
    mon tout nouveau blog

  6. #6
    Expert confirmé
    Avatar de Luc Hermitte
    Homme Profil pro
    Développeur informatique
    Inscrit en
    Août 2003
    Messages
    5 296
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Localisation : France, Haute Garonne (Midi Pyrénées)

    Informations professionnelles :
    Activité : Développeur informatique
    Secteur : Aéronautique - Marine - Espace - Armement

    Informations forums :
    Inscription : Août 2003
    Messages : 5 296
    Par défaut
    Sur le fonctionnement des compilos, la lvalue de destination n'a aucun impact et n'en aura jamais sur l'évaluation des expressions.

    La seule chose qui compte dans ton expression finale, c'est le numeric_limits<uint32_t>::max()+1, si tu veux autre chose, il faut caster le résultat de max. Et ça c'est spécifié: on cycle. Sauf si promotion....

    PS: si j'ai utilisé i != UMAX+1, c'est bien pour être sûr de ne pas avoir une condition toujours vraie à contrario de i < UMAX+1. Et il n'y a pas d'affectation. Juste une évaluation de sous-expression constante qui est la même que dans constexpr auto M = UMAX +1. L'évaluation de UMAX +1 ne peut pas et ne doit pas être contextuelle (avec ou sans affectation, employé dans une autre expression qui attend un truc ou pas). Si on commençait à faire ça, auto aurait des comportements différents de ce qu'il fait d'habitude. Et la règle est que la rvalue (constexpr ou pas) porte son type. C'est une règle simple d'induction de type dans un monde déjà assez complexe.

    Un test simple me montre que tous mes compilos sont d'accord (j'ai fait avec icc aussi, mais ça fait beaucoup côté écran): on cycle, sauf si la promotion fait boucler.
    https://godbolt.org/z/BfLMJj
    C'est le comportement nominal, et il ne faut pas qu'il change car il est "defined", et de fait, changer pourrait casser du code valide.

    Le problème avec std::integral_constant<T, std::numeric_limits<T>::max()+1>::value, c'est que MSVC fait comme si on écrivait std::integral_constant<T, T(std::numeric_limits<T>::max()+1)>::value et masque le narrowing, alors que les 2 autres râlent. Au mieux on peut avoir des warnings de ce dernier. N'est-ce pas le narrowing implicite des constexpr qui est UB en vrai et qu'il faudrait concrétiser comme une erreur? Ou alors le problème est que certaines implémentations de integral_constant font comme si on avait value = T{expr} et pas d'autres?
    https://godbolt.org/z/LLvNu-
    Blog|FAQ C++|FAQ fclc++|FAQ Comeau|FAQ C++lite|FAQ BS|Bons livres sur le C++
    Les MP ne sont pas une hotline. Je ne réponds à aucune question technique par le biais de ce média. Et de toutes façons, ma BAL sur dvpz est pleine...

Discussions similaires

  1. [AJAX] Tester les champs d'un formulaire avant de pouvoir l'envoyer
    Par italiasky dans le forum Général JavaScript
    Réponses: 1
    Dernier message: 02/05/2007, 12h25
  2. écrire dans un xml avant d'envoyer au serveur
    Par eloifi dans le forum Struts 1
    Réponses: 5
    Dernier message: 23/10/2006, 17h16
  3. [Upload] Renommer un fichier avant de l'envoyer
    Par wishmastah dans le forum Langage
    Réponses: 10
    Dernier message: 02/04/2006, 01h25

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