Quelques raccourcis :
Disclaimer
I wrote what follows mostly to keep my students informed of what a WG21 meeting is like, and I do not want to break the ISO code of ethics, but I do name people when I don't think it has any harmful effect (when discussions are more heated, I try to refrain from identifying individuals). Should you think it would be appropriate for me to remove things from this page, please tell me and I will proceed switfly.
Grâce à l'Université de Sherbrooke (en particulier, grâce à Jean Goulet et à Claude Cardinal du CeFTI) et au Collège Lionel-Groulx, j'ai eu le privilège de participer à WG21, le comité de standardisation ISO du langage C++, qui est mon principal outil de travail et d'expression (outre la langue française elle-même). Il s'agit d'un truc prestigieux pour les informaticien(ne)s comme moi, et j'ai l'honneur de faire partie de ce comité depuis la fin de 2014. Pour la rencontre de Lenexa (Kansas), ma toute première, je serai l'un des quatre délégués du Canada, les autres étant Michael Wong, Hubert Tong et Botond Ballo.
Ce qui suit est une sorte de journal de voyage, avec éléments humains et éléments techniques. Ce ne sera probablement pas aussi volumineux que ne l'étaient mes carnets de voyage pour CppCon 2014, car je serai là en tant que participant plus qu'en tant que « spectateur intéressé », mais j'essaierai de vous donner une idée de l'expérience.
Comme c'est souvent le cas dans les milieux scientifiques, le nom WG21 est peu spectaculaire. On parle ici du Working Group 21, soit le comité de standardisation du langage C++. Pour les informaticien(ne)s, c'est probablement ici que l'explication se suffit à elle-même, étant donné qu'on tombe directement dans leur créneau.
Pour la majorité d'entre vous, par contre, ça reste obscur, alors j'essaie une explication (il se peut que ça ne vous rejoigne pas, mais c'est de bon coeur) :
Le langage C++ existe depuis les années '80. Il a été standardisé par l'organisme ISO en 1998, puis solidifié en 2003 avec des ajustements (relativement mineurs) jugés nécessaires de par l'expérience concrète obtenue depuis C++ 98. Une révision très majeure du langage a été ratifiée en 2011, et C++ 11 est l'essentiel du langage par lequel nous exprimons nos idées aujourd'hui. Une révision mineure (mais fort appréciée) a été ratifiée relativement récemment (C++ 14), et la prochaine mise à jour majeure, C++ 17, est celle sur laquelle nous travaillerons à WG21 pour les prochaines années. Les objectifs de C++ 17 sont très ambitieux, et font saliver les gens qui, comme moi, cherchent à tirer le maximum de ce que ce langage a à offrir.
On me dit que nous sommes ≈300 membres de WG21 sur la planète, et qu'environ cent personnes se présentent aux rencontres du comité (les autres font surtout des contributions par écrit).
Pour un peu de bagage supplémentaire sur l'événement, voir :
Pour les journaux de voyage d'autres participants, voir :
Début du contenu technique
Je vais mettre le contenu technique dans des blocs les mettant comme celui-ci. Si vous ça ne tombe pas dans vos cordes, passez par-dessus.
Sur le plan de la procédure, certains débats étant chauds et les gens qui débattent étant mes pairs, je vais m'abstenir de donner dans le nominatif dans ces sections (à moins que ça n'ait pas d'incidence).
Fin du contenu technique
C'est aujourd'hui la fête de l'une de mes filles, l'adorable Vayda, qui donnait par-dessus le marché une prestation de Hip Hop remarquable en après-midi lors d'un spectacle ma foi fort relevé. J'ai eu peur de manquer sa prestation, l'avion le plus tardif que nous ayons trouvé pour m'amener à ma destination quittant Montréal vers 19 h 10, mais l'école de danse a eu la gentillesse de collaborer avec moi et de placer (suivant mon humble demande) Vayda et ses ami(e)s dans la première moitié du spectacle (j'ai donc pu quitter à la pause). Merci à mes chics beaux parents Jocelyn et Danielle pour avoir pris en charge le retour de Vayda, et merci à ma très chic maman Lucille pour s'être occupée de notre plus jeune, Ludo, qui n'aurait clairement pas tenu en place pendant le spectacle (si bon ce spectacle fut-il).
Évidemment, en quittant la voiture pour l'aéroport, la fermeture éclair du sac de voyage dans lequel je loge mes vêtements a cédé, et il m'a fallu me chamailler avec elle pour que le sac n'éparpille pas tous mes bas un peu partout dans l'aéroport ou dans l'avion. Évidemment encore, j'ai été fouillé en passant les douanes américaines, et il y a eu un début de panique en examinant mon ordinateur portatif (plein de craie, alors vous imaginez ce que les doûanières et les douaniers peuvent s'imaginer...). Je commence à être habitué, faut l'avouer, alors on a un peu blagué sur le sujet.
En attendant le départ, j'ai pu bavarder avec ma belle Za et avec les enfants les plus jeunes (Vayda et Ludo) par Skype, grand ami des voyageurs, bien que Ludo ait préféré les Télétubbies à son papa. J'ai aussi pu porter une attention distraite au deuxième match du Canadien contre Tampa Bay (c'était 1 à 1 quand je suis embarqué; j'espère avoir de bonnes nouvelles lors de mon escale à Chicago, en route vers Kansas City).
Dans l'avion, un petit CRJ200 (minimaliste, mais ça me va pour un voyage de moins de deux heures comme celui entre Montréal et Chicago; trop inconfortable pour quelque chose de plus long), ma voisine lit un livre électronique. C'est rendu très bien, ce truc. Agréable pour les yeux et tout. Non loin, un voyageur lit un volume de la fort sympathique série Saga, par Brian K. Vaughan, sorte de Roméo et Juliette semi futuriste mais très bien écrit et souvent surprenant (je suis accro par la faute de mon adorable épouse). Sur le chemin vers la piste de décollage, un beau coup d'oeil sur CAE électronique Ltéé, mon dernier employeur avant d'avoir pris le virage de l'enseignement. Je garde un excellent souvenir de mon passage à cet endroit; n'eût été de l'opportunité d'enseigner, j'y serais probablement encore.
Je profite du voyage pour lire quelques-uns des documents clés quant aux travaux qui nous attendent cette semaine (pour celles et ceux qui s'y connaissent un peu : les modules, les concepts, les coroutines, les intervalles, et quelques nouveautés franchement intrigantes). Dans chaque cas, on parle de propositions d'ajouts ou de modifications à la bibliothèque standard de C++, ou encore de changements au langage lui-même. C++ 17, qui sera notre objet de conception lors de cette semaine de rencontres et lors des réunions subséquentes du comité, et une mise à jour majeure du langage C++.
Le transit par Chicago fut très long, dû à un orage électrique sur l'aéroport
(l'avion a été immobilisé près de 40 minutes sur la piste). Étant donné que je
n'avais pas tout à fait une heure pour le transit de prime abord, ce fut un
transfert pour le moins rapide d'un avion à l'autre pour moi (moins de dix
minutes). C'est beau, cela dit, un orage vu des airs
L'avion de Chicago à Kansa City est un EMB170/175 d'Embraer. Même principe que le CRJ200, mais plus spacieux et beaucoup plus confortable. Le vol devrait être plus court cette fois alors j'en profiterai moins, mais je ne serai pas fâché d'arriver à destination.
J'ai pris le Shuttle de l'aéroport à l'hôtel. C'est une bonne distance : 45 minutes de route, 60$ plus pourboire. La dame était bien gentille et m'a laissé sa carte pour le retour; quand j'aurai une meilleure idée de mon horaire, j'essaierai de m'organiser avec elle pour le retour (je ne veux même pas penser au coût en taxi). En bavardant, j'ai appris que Kansas City est une ville de jazz, de grillades et, surtout, de fontaines. C'est la ville aux mille fontaines, en fait. Je crois comprendre que c'est dans la rivière Missouri que l'on puise une part importante de l'eau ici, mais il y a aussi plusieurs lacs dans la région. La ville « rivale », apparemment, est Saint-Louis.
Arrivé à l'hôtel vers 0 h 35 (1 h 35 à la maison; une seule heure de décalage avec la maison, c'est déjà ça de bien). J'ai écrit quelques mots à mon épouse, puis j'ai jeté un coup d'oeil aux résultats du Canadien... Semble que ça ne se soit pas bien passé pour nous ce soir. Eh ben... Espérons qu'ils se relèvent rapidement!
Demain, on se met au boulot...
P.-S. : je viens de faire le compte et je pense que j'ai perdu une paire de bas en chemin, sans doute pendant la période où mon sac n'avait pas de fermeture éclair. Hum...
Le lobby de l'hôtel n'offre pas de repas mais on y trouve du Starbucks. Comme c'est souvent le cas avec cette chaîne, c'est bon, mais c'est un peu cher, soit 8 USD avec les taxes pour un café et un sandwich oeuf + bacon + fromage réchauffé au micro-ondes (il était Ok, cela dit). Quand je suis allé à Niagara Falls il y a quelques années pour présenter un de mes articles, la seule chose « non-junk » qu'il y avait était un Starbucks, et j'avais mangé un casseau de fruit chaque matin, mais c'était pas moins cher.
L'hôtel est assez gros, mais c'est difficile d'estimer sa taille de là où je suis. J'ai croisé quelques personnes dans le lobby qui vont probablement à la même place que moi. J'attends encore de savoir à quelle heure la navette quitte pour le lieu des travaux. La bonne nouvelle est qu'il y a une laveuse + sécheuse au deuxième étage, alors je pourrai faire un peu de lessive vers le milieu de la semaine.
Le tour de navette fut amusant. Nous avons quitté l'hôtel vers 8 h dans l'une des navettes (il en fallait quelques-unes car nous sommes assez nombreux). Le véhicule était bondé de sommités, tous plutôt sympathiques. J'étais assis à côté d'un gars qui travaille sur le compilateur C++ d'Oracle (autrefois de Sun) et qui remplace sur WG21 un individu qui vient de prendre sa retraite. Sur le banc juste derrière nous, Eric Niebler expliquait à un participant là où il en est avec ses travaux sur les intervalles.
Un petit déjeuner était offert par sur place par notre hôte, Perceptive Software (ceux
qui font les imprimantes Lexmark entre autres); je retiendrai ça pour les
prochains jours. Les lieux sont très beaux, aérés et bien équipés. On y trouve
même une glissade rotative, mais faut signer un document pour l'utiliser
. Ce
déjeuner nous a donné un peu de temps pour bavarder. J'ai piqué une jasette
entre autres avec Mike Spertus, qui enseigne à l'université de Chicago, de même
qu'avec un Finlandais et un brésilien, tous de chics types.
Le lieu de rencontre est dans un bâtiment adjacent. Faut passer par l'extérieur, mais il fait beau alors c'est pas grave. C'est un peu chaud pour moi mais on travaille essentiellement à l'intérieur alors c'est gérable.
Comment fonctionne une rencontre du WG21? Voici ma petite expérience de cette première journée (je ne sais pas si c'est toujours comme ça).
Nous avons débuté par une session plénière. On commence à
9 h, heure locale. À l'oeil, on est à peu près 120-150 personnes
On fait circuler une feuille de présences. Cela permet
entre aux gens de savoir qui participe à quoi
Nous avons eu droit à petit laïus sur les règles d'éthique
et de gestion des conflits d'intérêt : on doit les montrer, les respecter, mais
on ne peut les discuter ici. C'est important
Le droit de vote aux WG21 straw polls
est accordé à tous les
représentants officiels d'un pays
Les gens se présentent un à un (incluant moi) et disent qui ils représentent. Il y a trois dames, seulement, si j'ai bien compté, mais malgré ce déséquilibre (qui m'agace, celles et ceux qui me connaissent s'en doutent), le calibre intellectuel de la salle est pour le moins impressionnant.
Il nous faut adopter un « ordre du jour » pour la semaine (un « ordre de semaine »?). Des gros travaux :
Le rapport de liaison (avec les instances d'ISO) est fait par Herb Sutter. Il explique les changements de procédure et propose un portrait des travaux possibles cette semaine.
Les prochaines rencontres prévues pour WG21 sont Kona, Hawaii, en octobre 2015, et Oulo, au Nord de la Finlande, probablement en juin 2016. Il se peut qu'une rencontre intercalaire apparaisse du fait qu'il y a huit mois entre les deux, ce qui est beaucoup
Certains des documents qui seront votés cette semaine pourraient être carrément publiés à la fin de la semaine en tant que documents officiels. Les procédures d'ISO sont plus agiles que par le passé, mais en retour, faut être prudents. Les concepts, en particulier, sur lesquels j'ai travaillé ces derniers mois, sont proches d'être adoptés. Le document http://www.open-std.org/jtc1/sc22/wg21/docs/papers/2015/n4437.pdf est un exemple de ce que nous pourrions faire rapidement pour C++ 17 (maths spécialisées, implémentation optionnelle)... L'air de rien, pour arriver à , on aura 3 ou 4 rencontres max. Ça va vite!. Informellement, on est un peu trop dynamiques ici pour la procédure « officielle » d'un standard ISO, mais on s'entend pour rester informels et dynamiques.
Pour la propriété intellectuelle, les Working Drafts et les documents formellement publiés appartiennent à ISO. Les documents que nous proposons sur une base individuelle nous appartiennent entre-temps. Il arrive que WG21 finance en partie des travaux pour produire des documents pour fins de standardisation (Eric Niebler en a bénéficié pour son superbe travail sur les intervalles). Voir https://isocpp.org/blog/2015/05/financial-assistance-policy pour plus de détails.
Un participant demande si la nouvelle spécification sur les nombres à virgule flottante pour le langage C (leur groupe est WG14) pourrait nous revenir sous peu. Plusieurs personnes dans la salle participent à ça. C'est pas clair qu'on souhaite l'intégrer à C++ (plus ou moins à propos). Sais pas quel impact cela aura sur l'interopérabilité entre les langages, cependant (en fin de journée, j'ai discuté avec Hubert Tong, qui siège aussi sur WG14 et qui m'expliquait que C accepte maintenant des nombres à virgule flottante sur 128 bits, mais sans leur imposer un nom fixe et de manière conditionnelle, sur la base de certaines macros. En gros, la compatibilité entre C++ et C réduit de plus en plus).
Les groupes de travail pour cette semaine seront les suivants :
N'étant pas familier avec le fonctionnement de tout cela, je me demandais où aller. Walter Brown a recommandé aux gens de participer à LWG, où il y a typiquement moins de participants que dans les autres groupes, et j'étais ouvert à cela, mais en bavardant avec Michael Wong, j'ai plutôt fait le choix de m'intégrer à CWG du fait que j'ai participé activement à la relecture des TS sur les concepts et sur la mémoire transactionnelle. Pour cette raison, il est préférable que je sois présent pour clarifier ma position si besoin est.
J'escamote ici une part importante d'échanges informels sur l'avancement de dossiers du WG21 en comparaison avec les habitudes et les usages des comités ISO. Nous avons pris une brève pause vers 9 h 30, heure locale, pour nous déplacer vers des locaux distincts en fonction des groupes de travail.
Je me suis donc intégré à CWG avec plusieurs sympathiques personnes, dont Botond Ballo, Jens Maurer, Jonathan Caves, Walter Brown et Hubert Tong, de même que le chic type d'Oracle rencontré ce matin. On est dix-sept ce matin, je dirais (note : le nombre varie selon les sujets discutés, mais un groupe relativement stable demeure). On aura une séance commune avec EWG cet après-midi.
Ce matin, on fait le tour des commentaires soumis récemment. Plusieurs sont surtout « éditoriaux ». Ça se règle vite. Pour les autres, c'est plus complexe, d'où la séance conjointe.
Le sujet chaud de la journée pour nous est le TS des concepts, la plus récente version étant http://www.open-std.org/jtc1/sc22/wg21/docs/papers/2015/n4377.pdf et dont l'auteur, Andrew Sutton, n'est pas parmi nous aujourd'hui. J'ai participé de près au processus de relecture de ce document, et j'ai beaucoup échangé avec Botond Ballo au cours des deux ou trois dernières semaines pour que nous en arrivions à une compréhension plus fine des enjeux. Les votes des pays ne sont encore pas tous soumis, mais nous avons déjà les contributions britanniques, finlandaises, canadiennes et américaines (ce dernier document étant plus volumineux, et aussi plus contentieux).
Un « secrétaire » à distance nous écoute et prend des notes. Localement, un membre présent met un Wiki (document commun) à jour sur-le-champ pour tenir compte de l'évolution des travaux. Au besoin, nous échangeons avec des experts externes par courriel ou par clavardage, incluant l'auteur du TS dont nous discutons, pour clarifier ceci ou cela.
Plusieurs éléments débattus sont d'ordre « éditorial », au sens où les changements demandent d'être vérifiés mais ne sont pas vraiment source de conflits d'idées. Ajouter un mot ici, en enlever un là, clarifier un exemple quand il manque quelque chose de manière évidente (et ce n'est pas toujours évident de savoir s'il y a une erreur quand on parle d'un document qui exprime des idées neuves; parfois, c'est simplement une subtilité qui nous échappe et il faut en débattre pour en arriver à une acception commune), etc. Je ne m'attarderai pas sur eux ici.
Les sujets importants qui seront débattus aujourd'hui sont :
Nous avons tout d'abord fait un survol rapide de tous les commentaires des quatre documents reçus, pour les catégoriser en fonction du traitement à leur accorder (traiter en CWG seulement, ou conjointement avec EWG, ou simplement remettre à l'éditeur du TS pour retouches mineures).
Les travaux ont suivi leur cours jusqu'à midi, à quel moment nous avons pris une pause. Nous avons eu le temps de traiter les commentaires canadiens, britanniques et une partie des commentaires finlandais pendant l'avant-midi.
Nous avons été informés ce matin que, de manière exceptionnelle, le dîner serait fourni par l'entreprise hôte, qui nous a préparé quelque chose de local : grillades, sauce BBQ, salade de choux, maïs dans une crème fromagée, pain. C'est quand même assez lourd, mais c'était goûteux. Le dessert était du gâteau au chocolat, apparemment moelleux mais je n'en ai pas pris.
En début d'après-midi, vers 13 h 30, nous avons repris les travaux dans une salle commune où se regroupaient les gens de CWG et d'EWG. L'idée est de voir ce qui regroupe les responsabilités des deux groupes de travail, en particulier pour ce qui est plus sujet à des débats plus rudes.
Je mets cette rencontre commune dans un encadré distinct, car ce fut une rencontre sur un ton plutôt « costaud ». Ce qui suit est un résumé de deux heures de discussions intenses.
Question : pourquoi ne peut-on pas déclarer des concepts locaux à un template de classe? La remarque tenait au fait que cete restriction empêche de contraindre certains services variadiques. Semble que cette contrainte soit présente pour permettre une expansion hâtive d'un template, et qu'il soit possible d'utiliser enable_if pour les contraintes résiduelles. Les arguments clés du débat :
Quelqu'un mentionne qu'il n'existe pas de concepts récursifs dans la proposition. On se demande si on devrait y réfléchir aujourd'hui.
Résolution : le consensus semble être de remettre ça à une autre rencontre
Note personnelle : à quoi un concept récursif ressemblerait-il? J'ai des idées…
Question : peut-on utiliser des concepts partout, incluant dans les types de retour de fonctions et comme type de variables? Le TS existant ne le permet pas.
Résolution : on convient d'accepter ette fonctionnalité pour voir si l'expérience portera fruit
Question : devrait-on permettre d'utiliser les clause requires partout, à titre de prédicats statiques, plutôt que de les restreindre aux seuls concepts?
Résolution : on va attendre. Ça ne fait pas l'affaire de tout le monde
Question : permet-on de résoudre une clause requires sur la base d'une spécification noexcept? Si oui, accepte-t-on la totalité des expressions noexcept ou se limite-t-on à la présence d'une telle spécification?
Résolution : rejet de la proposition (vote majoritaire)
Question : devrait-on supprimer le type bool des concepts et l'y considérer implicite?
Résolution : on remet ça à plus tard
Question : dans les clauses requires, devrait-on permettre une syntaxe =>T pour « retourne quelque chose qui soit précisément du type T » à la syntaxe existante ->T pour « retourne quelque chose dont le type soit implicitement convertible en T »?
Résolution : on rejette, mais certains ne sont pas contents
Après ces échanges, nous sommes retournés dans une salle à part pour compléter les travaux tombant sous la « seule » responsabilité de CWG. Nous avons fini d'éplucher les commentaires finlandais et américains. Certaines questions subtiles devaient aussi être débattues, comme par exemple : une clause requires{...} où les expressions entre { et } évaluent toutes à faux de manière démontrable mène-t-elle à un programme mal formé? Comment exprimer clairement la distinction entre les contraintes des concepts et les expressions booléennes qui s'y retrouvent?
Par la suite, la question est celle des suites :
Gabriel Dos Reis nous laisse entendre que Kona sera une rencontre très chargée, et dit qu'on en saura plus à la rencontre de ce soir.
Nous souhaitions ensuite discuter de mémoire transactionnelle mais Jens Maurer a dû nous quitter, et sa présence nous semblait essentielle. Nous avons donc profité du moment pour aborder des questions grammaticales et terminologiques, comme par exemple : quel nom donner aux fonctions comme les constructeurs, destructeurs et opérateurs de conversion, qui ont une syntaxe différente des autres? Et comment exprimer cette différence de manière non-ambiguë?
Sur « l'heure du souper », les discussions se poursuivent. J'écris ce que vous lisez ici (j'ai pas soupé, en fait). La séance de la soirée débute vers 19 h 45 finalement. C'est Bjarne Stroustrup qui prend le plancher et présente sa vision de ce que devrait être C++ 17. En gros :
On se bat contre les rumeurs et les préjugés. Par exemple, tiré d'un article récent : « nous avons utilisé D parce que C++ n'a pas de fonctions résolues à la compilation... »... euh... Quoi? Bjarne Stroustrup critique les enseignant(e)s qui ne se tiennent pas à jour selon lui, et enseignent un C++ qui ne ressemble pas à ce que nous leur proposons depuis longtemps.
Objectifs de haut niveau de C++ 17 :
Il faut aussi en général améliorer l'image du langage sur le plan de la résilience et de la stabilité.
Selon Bjarne Stroustrup, les trucs comme auto et les répétitives for sur des intervalles sont de grands succès de C++ 11. Simplifier la programmation est un objectif continu.
Il ne faut pas négliger nos forces fondamentales (lien direct avec le matériel, abstraction à coût zéro, ce genre de truc). C++ doit demeurer C++.
On veut compiler plus vite et réduire la dépendance envers les macros. Pour ça, on vise les modules, les contrats et les union Type-Safe qui nous rapprocheront du Pattern Matching que l'on voit dans les langages fonctionnels et nous permettront de nous éloigner des sélectives sur des unions discriminés.
On veut un meilleur modèle de concurrence. Plusieurs visions sur sur la table.
On veut un langage plus simple à utiliser, et moins d'erreurs. Les concepts et les intervalles vont en ce sens, mais aussi l'équivalence entre appels de fonctions et de méthodes, la surcharge de l'opérateur . (pour faire des références intelligentes), les vues, les type optional<T>, etc.
On suppose que C++ 17 sera livré en 2017, alors on ne vise que ces gros morceaux que l'on pense pouvoir livrer à temps. Bjarne Stroustrup parle de STL 2, ce qui serait gros en soi. Uniformiser les syntaxes d'appel élimine une distinction un peu inutile entre les approches OO et fonctionnelle.
Comment y arrriver? Les gens ici sont volontaires, bénévoles. Faisons de notre mieux pour 2017 et faisons quelque chose de fort; ce qui déborde ira en 2020, comme C++ 14 a complété C++ 11. Faut absolument éviter de se limiter à un standard mineur assemblant quelques TS. Faut éviter la fragmentation, les cartels ou l'apathie.
Alisdair Meredith : je suis plus conservateur, dans un autre sens. À mes yeux, C++ 14 était beaucoup plus important que prévu. Je crains que si nous forçons trop pour C++ 17, nous ne tarissions le pipeline que nous avons prudemment construit.
Chandler Carruth : je suis sympathique à ce que j'entends, mais ma perspective est différente. Ceux avec qui je parle, même les plus critiques envers le langage (il sous-entend les gens de Go et de Dart), sont plus heureux de C++ qu'ils ne l'ont jamais été depuis C++ 14. Je ne crains pas une chute de satisfaction envers C++, qui brille plus que jamais, et je pense que nous ne devrions pas craindre ce que nous pouvons perdre. Je préférerais que l'on examine ce que nous pouvons apporter de mieux.
John Lakos : je suis content qu'on parle de contrats, et je pense que peu de gens comprennent vraiment ce que ce terme signifie. Je vais travailler fort avec Gabriel Dos Reis pour clarifier notre propos et faire comprendre pourquoi c'est important à nos yeux. J'aimerais aussi demander aux gens présents ici ce qu'ils pensent du terme Vocabulary Type, en tant qu'entité distincte de Standardized Type?
Bjarne Stroustrup : je pense que la bibliothèque standard offre des Vocabulary Types, mais qu'on ne peut pas tout standardiser non plus.
Alisdair Meredith : on est supposés livrer C++ 17 en version préliminaire suite à Kona.
Herb Sutter : on veut au moins deux rencontres pour débattre du standard proposé sans y ajouter de nouveaux éléments majeurs, donc Kona serait le moment pour se mouiller.
Jeffrey Yasskin : j'aime l'agenda accéléré. Je suis à l'aise de briser la compatibilité pour intégrer les concepts. Je crains que C++ 17 soit trop tôt pour le faire de manière à ce que cela rapporte.
Chandler Carruth : à mes yeux, les modules sont le nouveau morceau qui sera porteur des plus fortes conséquences. C'est épeurant. Pour ceci comme pour les concepts, je crains que C++ 17 ne nous mène à une situation où les principaux vendeurs de compilateurs n'arriveraient pas à livrer des implémentations adéquates à temps.
Gabriel Dos Reis : je travaille présentement à implémenter les modules sur le compilateur de Microsoft. C'est un défi, mais je n'y vois rien qui soit insurmontable. À mon avis, notre spécification est raisonnable... dans la mesure où on ne vise pas nécessairement à supporter d'autres langages que C++.
Bjarne Stroustrup reprend le micro. Il dit ne pas se préoccuper par les autres langages, mais se préoccupe par contre de maintenir l'allure des travaux (le momentum). Il exprime ses craintes quant aux mauvais côtés du design par comité (faire ce qui est plus simple; chercher à plaire à tout le monde; passer plus de temps sur le texte que sur les fonctionnalités; etc.).
Chandler Carruth : je n'ai pas de Top 10 d'objectifs pour C++ 17, mais j'ai une préoccupation. Cette fois, on a sur la table beaucoup de changements de langage mais relativement peu de changements de bibliothèques. Ce n'est pas le but que nous avions placé en priorité il y a quelques années. Nous sommes en 2015 mais nous n'avons pas de moyen standard de connecter un programme à un serveur Web! Heureusement, nous avons une proposition solide sur la table, mais il était temps.
Bjarne Stroustrup se dit d'avis que les TS sont un modèle qui fonctionne mieux pour les bibliothèques. Nous devons rester vigilants quant à la cohérence des TS et quant à leur interopérabilité. Selon lui, WG21 devrait travailler sur les bibliothèques « fondationnelles », incluant celle sur la réseautique, plutôt que sur des bibliothèques de consommation de JSON ou de XML.
Chandler Carruth : pourtant, nous constatons que la réseautique exige de nous de mettre de l'avant une sorte d'objet exécuteur. Idem pour le parallélisme. Nous n'avons par contre pas de bon moyen pour construire du TS sur la base d'autres TS sans les intégrer au standard.
Jeffrey Yasskin : je ne suis pas convaincu que nous ayons de si gros problèmes quant à l'interaction des TS.
Bjarne Stroustrup : je ne veux pas qu'on craigne d'être imparfaits.
Herb Sutter : les Vocabulary Types, pour moi, sont des types qui utilisent une interface comprises par tous (il en mentionne quelques-uns, dont les pointeurs intelligents, les vues et optional<T>). Les modules sont pour moi une technologie pour enfin permettre la conception de véritables outils de gestion de paquetages. La mise en commun des nouveaux éléments de C++ avec C++ 17 sont là pour nous permettre de faire plus et de faire mieux.
Mike Spertus : on contribue tous à ce qui compte pour nous et pour notre employeur. Sur le plan des processus, la question est de trouver comment polariser nos efforts sur quelques points clés quand nos intérêts sont diversifiés.
Quelqu'un mentionne que le simple fait d'amener les modules impressionnerait la communauté toute entière.
Herb Sutter mentionne que sans les intervalles d'Eric Niebler et de tels travaux, nous n'aurons pas les concepts du tout. Les concepts doivent être utilisés pour être utiles.
Alisdair Meredith : je ne suis pas certain que deux rencontres par année est suffisant pour faire avancer autant de travaux. Il nous faut des rencontres face à face pour adresser certaines questions.
Bjarne Stroustrup : à mon avis, cela a donné naissance à plus de rencontres en petits groupes. Je ne suis pas si sûr que l'on perd au change.
Hubert Tong : nous voulons réduire les divergences d'implémentation, mais en même temps nous vivons avec ces divergences au quotidien. Je ne suis pas certain que WG21 aide suffisamment ses implémenteurs.
Herb Sutter : je n'ai jamais vu C++ aussi unifié, personnellement. Presque tous les compilateurs majeurs sont pleinement conformes à C++ 14 déjà. L'incertitude déprime les implémenteurs; des mises à jour régulières du standard, c'est sain.
Bjarne Stroustrup : nous n'éviterons pas les bogues. Il nous faut un processus de gestion des erreurs et des correctifs. Mieux vaut livrer.
Quelqu'un qui travaille sur la bibliothèque standard dit : donnez-nous le TS des concepts, nous vous livrerons la bibliothèque standard avec concepts. Nous voulons les concepts!
Hubert Tong : nos clients veulent pas contre que nos mises à jour ne brisent pas leur code. Personne ne veut des inclusions conditionnelles sur la base de la version du langage supportée par un compilateur ou l'autre.
Quelqu'un du monde HPC (High Performance Computing) : nous ne pouvons utiliser std::vector qui perd certaines informations clés d'optimisation. Nous ne pouvons utiliser plusieurs conteneurs standards car les versions intrusives sont plus rapides.
Bjarne Stroustrup : c'est sur ma liste. Il nous faut ça. Je souhaite un groupe de travail sur ce sujet.
Chandler Carruth : le standard doit être suffisamment stable, et les implémentations suffisamment convergentes, pour qu'un client puisse écrire des programmes qui tiendront la route. Nous devons être prudents et respecter nos clients. Il n'est pas temps de mettre les freins, mais c'est un défi qu'il nous faut considérer.
Bjarne Stroustrup : c'est sur ma liste aussi : stabilité et conformité.
Quelqu'un demande comment nous souhaitons vraiment impressionner la communauté, dans une optique de stabilité.
Bjarne Stroustrup : les modules et les concepts, à mon avis, seront impressionants.
Herb Sutter : notre rôle est d'écrire les spécifications qui permettent aux gens d'écrire des produits avec confiance. Depuis 10-15 ans, les programmeuses et les programmeurs n'ont pas beaucoup augmenté en nombre sur la planète. Sur le groupe, un 30% utilisait C++ en priorité ou presque en début de parcours. (il lance la question : le groupe réduit-il? Est-il stable? Grandit-il?) On est presque rendus à 40%. Ce n'est pas par des trucs comme TIOBE que l'on obtient des chiffres sérieux. Nous avons de quoi être fiers; faut continuer de pousser vers l'avant.
John Lakos parle de la difficulté de faire adopter les changements dans une entreprise. Bjarne Stroustrup dit vivre la même chose.
Bjarne Stroustrup demande aux gens de lui soumettre leur Top 10.
On ferme les livres vers 21 h 10 heure locale. On est tous brûlés. La climatisation est déficiente ce soir.
Sur le chemin du retour, j'ai eu comme voisin de siège d'autobus le chic brésilien avec lequel j'avais bavardé le matin-même. Il se trouve qu'il est l'auteur de l'excellente (vraiment) proposition sur la réflexivité statique qui m'avait fait fortement sourire lors de sa publication. Comme bien des brésiliens que j'ai eu le plaisir de rencontrer, il parle doucement mais est très passionné. J'aime l'écriture qu'il propose, personnellement. Espérons que sa proposition fasse son chemin!
Au retour à l'hôtel, je vais devoir faire un peu de lessive (déjà, je sais, mais il fait chaud ici et les journées sont longues alors je vais manquer de chemises demain!). Auparavant, chose plus urgente : je prends des nouvelles de la maison. Évidemment, c'est quand je dois m'absenter que le monde s'écroule, que les enfants sont malades et que les animaux de compagnie se portent mal.
Ma famille me manque, et mon épouse a les mains pleines avec les animaux et les enfants qui sont tombés malades en même temps. Ouf. Courage, je pense à vous!
Ce matin, classique pour moi, je me suis pris un café et j'ai oublié de demander un reçu. L'art de perdre des sous... Surtout chez Starbucks, où le café est démesurément dispendieux.
J'ai recroisé mon ami brésilien, Cleiton Santoia Silva, ce matin. Brillant bonhomme. On a bavardé enfants (il a une cocotte de cinq ans), vie à Sao Paulo (d'où il vient, petite ville de 30 millions d'habitants) et corruption politique. Il est un peu nerveux car la séance plénière de ce soir portera sur la réflexivité, l'un des dossiers pour lesquels il apporte une vision personnelle.
Ce matin, pas de plénière. On va directement aux groupes de travail, et je suis retourné à CWG car nous devons parler de mémoire transactionnelle, un dossier auquel j'ai contribué, mais le gros sujet (celui qui attire la plupart des participants) est celui des modules, débattu chez EWG.
Pour la mémoire transactionnelle, le document discuté est http://www.open-std.org/jtc1/sc22/wg21/docs/papers/2014/n4302.pdf. Les commentaires sont rassemblés dans http://www.open-std.org/jtc1/sc22/wg21/docs/papers/2015/n4396.pdf, et le feedback de Jens Maurer se trouve dans http://www.open-std.org/jtc1/sc22/wg21/docs/papers/2015/n4410.html.
Les suggestions et commentaires sont traités un à un, et les résolutions suivent pour la plupart ce qui découle de l'analyse de Jens Maurer. Dans certains cas, on recommande de faire suivre les recommandations à LWG pour s'assurer qu'ils soient confortables aussi puisque ça les touche. Dans d'autres cas, comme le changement d'un mot clé hypothétique (de transaction_safe_noinherit à transaction_safe_dynamic), on relaie la recommandation à EWG. Notez que le choix d'un mot clé ne rencontre presque jamais l'assentiment de tous; c'est un truc périlleux, avec lequel il faudra vivre longtemps.
La mémoire transactionnelle est un sujet subtil, qui touche à la question de savoir ce qui peut être déterminé quant à l'ordonnancement des opérations dans un programme parallèle ou concurrent sur la base de son texte. Par exemple, certains libellés laissent entendre qu'il soit possible de déterminer une relation happens-before entre deux instructions situées dans deux blocs distincts s'exécutant concurremment; cela va de soi dans un même thread, mais sinon cela suppose un hypothétique ordonnancement total entre plusieurs threads ce qui est beaucoup moins évident. Pour cette raison, certaines résolutions causent un léger inconfort, mais rien de suffisant pour bloquer l'avancement du dossier.
Dans certains dossiers nouveaux ou expérimentaux, comme celui de la mémoire transactionnelle ou celui des concepts, le support des nouvelles fonctionnalités peut typiquement être validé par des directives d'inclusion conditionnelle, ce qui implique la standardisation de macros pour indiquer le support ou non de chaque fonctionnalité, sur une base individuelle, et de valeurs (numériques, avec croissance monotone) associées à ces macros pour permettre une forme de contrôle des versions. Le choix de ces macros, de même que leur présentation dans les documents comme les TS (car ce qui est standard est supposé être supporté, point à la ligne, alors que les TS peuvent l'être ou non pendant une période d'essai) demande un effort de formalisme et de cohérence entre les documents. Pour cette raison, nous discutons un peu avec Clark Nelson, le chairman de WG21.
Deux de mes propositions sont acceptées de facto. Une autre
(question de clarification terminologique) est refusée, ce qui est correct.
Moment amusant : l'un des individus, voyant ce document, passe une remarque sur
ces idiots d'universitaires qui ne comprennent rien, ce à quoi je me suis
permis de répliquer « en passant, l'idiot d'universitaire, ici, ce serait
moi ». Disons que ça a détendu l'atmosphère
Parfois, des trucs subtils surgissent. J'avais souligné l'impossibilité d'écrire un swap() qui serait Transaction-Safe avec l'écriture courante car std::move(), comme plusieurs autres fonctions « utilitaires » du genre, n'était pas considéré Transaction-Safe. On avait généralisé mon commentaire pour couvrir l'ensemble des fonctions utilitairs, mais Alisdair Meredith a allumé ce matin que l'on couvrait aussi declval<T>() avec cette recommandation, or cette fonction ne sert que dans un contexte statique (elle n'est jamais appelée) ce qui rend impertinent le fait que la rendre Transaction-Safe, les transactions étant un mécanisme foncièrement dynamique.
Autres trucs subtils :
Il semble qu'il y ait eu une alerte de feu dans l'autre bâtiment où se déroulent les travaux de WG21. Nous y avons échappé.
Échanges intéressants avec Botond pendant la pause. Il était à la rencontre d'EWG sur les modules, qui se poursuit d'ailleurs après la pause. Selon lui, deux philosophies s'affrontent :
C'est un sujet qui attise les passions.
Ensuite, nous avons discuté de plusieurs sujets subtils mais pointus, découlant de remarques soumises par des gens ayant « frappé des noeuds » dans le standard (incohérences, trucs obscurs, ambiguïtés, etc.). Selon l'écriture du standard telle que nous l'avons avec C++ 14 :
L'expression new(nothrow)int[N] pourrait lever une exception convertible en bad_array_new_length si N est incorrect. La phrase est complexe, alors on travaille sur la lisibilité
Le standard utilise parfois les expressions happened-after et sequenced-after mais ne définit que les expressions happened-before et sequenced-before, plus près de la littérature. Une option est de reformuler les diverses occurrences de ...-after en ...-before, mais c'est périlleux et il y en a beaucoup. Nous allons plutôt dans le sens de donner un sens formel à ...-after, sur la base de ...-before par souci de cohérence. On a sûrement manqué des cas de spécification d'ordonnancement dans le standard, mais étant donné l'ampleur de la tâche, il est sans doute préférable d'attendre que certains remarquent des irritants avant de s'investir dans un passage du texte de près de 2000 pages au peigne fin
Cas très subtil : comment identifier la conversion appropriée vers un type commun dans un cas d'opérateur ternaire (forme expr ? a : b) tout en tenant compte des diverses formes de références et des trucs obscurs comme les bitfields. Le standard ne semble pas couvrir correctement les cas que l'imagination des programmeuses et des programmeurs a pu faire surgir, mais ce que nous avons sur la table ne satisfait pas non plus. On va continuer de travailler sur celle-là.
Truc obscur mais qu'il faut gérer : les spécifications d'exceptions sur les expressions λ (dépréciées mais pas disparues).
Autre truc obscur : la possibilité de faire des macros qui bousillieraient les directives #include. C'est un irritant auquel C fait aussi face.
Truc obscène : écrire des fonctions ou des constructeurs constexpr mais dans lesquels interviennent des éléments variants mutables. Sans commentaires. Faut trouver une formule pour dire non.
Truc amusant : il n'y a pas beaucoup de restos dans le secteur et la bouffe est à nos frais, alors je suis allé à la cafétéria de l'entreprise qui nous accueille. On y trouve un bar à salade pas si mal : on se sert, ça fonctionne au poids, on paie nous-même (pas de caissière) et on s'envoie à soi-même un reçu par courriel. Comme bien des gens ici, je travaille en mangeant. Cependant, une heure plus tard, je n'ai toujours pas « reçu mon reçu » alors je m'inquiète un peu...
On reprend les travaux vers 13 h 30. Il semble que les échanges sur les modules ne se soient pas rendus au point où ils ont pu discuter des propositions... Ouch!
Discussion sur la proposition http://www.open-std.org/jtc1/sc22/wg21/docs/papers/2015/n4430.html par Richard Smith, sur la nature des pointeurs et des pointés, et le lien entre pointeur et objet (au sens C++ du terme).
On a quatre cas :
X x;
auto p = &x;
auto q = &x+1; // p pointe sur l'objet, q pointe juste après l'objet
auto p = new X;
delete p; // suite à la destruction de *p,
p est invalide
Nous avons débattu de questions subtiles comme : après la mort d'un objet, peut-on encore dire que le pointeur y pointant pointe sur un objet? Selon Richard Smith, la réponse est oui, mais sur un objet dont la vie est terminée.
La mémoire brute devrait apparaître comme un tableau d'unsigned char pour permettre l'arithmétique de pointeurs.
La vie d'un objet débute quand ses attributs sont tous initialisés. Cela dit, quand débute la vie d'un union? Ce n'est pas évident puisque certaines parties peuvent ne pas être initialisés. Richard Smith propose que l'affectation d'une valeur à un membre d'un union fasse de ce membre le membre « actif » de l'union. C'est important pour les cas où on passerait l'adresse d'un membre de l'union à une fonction, ou pire : dans le cas où un comique passerait l'adresse de deux membres distincts d'un même union à une fonction, genre :
union U
{
int i;
char c;
};
void f(int&, char&);
int main()
{
U u;
// ...
f(u.i, u.c); // duh???
}
Ouch! La détermination du membre « actif » de l'union iintervient aussi pendant sa construction, s'il y a lieu.
Avec cette proposition, retourner un pointeur sur une variable locale deviendrait un retour d'un pointeur invalide, formellement, ce qui deviendrait utile sur le plan des diagnostics. Jens Maurer fait remarquer que l'optimisation Copy Elision contournerait cette nouvelle considération en enlevant la copie du portrait (ouch!), mais on peut toujours décider de ne pas appliquer cette optimisation dans le cas où le pointeur retourné par copie serait invalide.
La proposition formalise aussi des trucs comme :
struct X
{
int m;
};
int main()
{
X x;
assert((void*)&x==(void*)&x.m); // Ok
reinterpret_cast<int*>(&x); // incorrect
}
... ou encore comme :
union U
{
int i;
char c;
};
int main()
{
U u;
assert((void*)&u.c == (void*)&u.i); // Ok
reinterpret_cast<int*>(&u.c); // incorrect
}
Richard Smith introduit la fonction std::launder()
pour obtenir un pointeur distinct sur un pointeur existant. C'est pervers,
et faudra la passer à LEWG, celle-là (le gag, c'est
d'essayer d'imaginer leur réaction
). L'idée
ici est de permettre les malpropretés (explicites) comme ce qui suit, quand
quelqu'un sait vraiment ce qu'il fait. L'exemple et les commentaires sont tous de
Richard Smith :
template <class T> constexpr T* launder(T* p) noexcept;
// ...
struct X { const int n; };
X *p = new X{3};
const int a = p->n;
new (p) X{5}; // p does not point to new object ([basic.life])
// because X::n is const
const int b = p->n; // undefined behavior
const int c = std::launder(p)->n; // ok
C'est sûrement l'un des trucs les plus intéressants, mais les plus pervers, que j'aie rencontré jusqu'ici cette semaine. On pourrait entre autres utiliser launder() pour implémenter optional<T>, dont j'ai une version maison (non-commerciale) avec maybe<T>, en utilisant à l'interne l'expression :
*launder(reinterpret_cast<T*>(&buf[0]))
... pour traiter buf, un unsigned char[sizeof(T)], comme un T&, mais ceci :
union // anonyme, c'est volontaire
{
T obj;
char buf[sizeof(T)];
};
...est préférable pour faciliter le recours à constexpr.
Question ouverte : devrait-on ajouter un launder() implicite à reinterpret_cast? Je pense aux fonctions du système d'exploitation qui passent par un void* ici. On réfléchit... C'est pas propre, c'est déjà illégal (même si personne ne le sait et même si ça tend à passer pareil). Quelle est la pratique qu'on veut encourager ici?
Jonathan Caves parle de launder() comme « hey, le compilateur, ferme donc tes yeux pour une seconde ». On rigole un peu.
On passe ensuite à la clarification du sens à donner à des expressions impliquant void ou void* avec qualifications cv. Ça peut arriver avec des conversions à void dans des expressions comme (void)a,b par exemple, comme on en voit dans certaines implémentations de la STL pour contourner des pièges tendus par des gens qui auraient surchargé l'opérateur ,.
On jase sur la possibilité de distinguer void f(void) et void f(const void), mais semble qu'aucun compilateur connu ne supporte cette horreur.
Richard Smith dit qu'on lui aurait laissé entendre que la déclaration extern void x; serait légale. Hubert Tong indique que oui, en C); si on ne la définit pas, c'est une déclaration sans définition, ce qui devient un bris (inverse) d'ODR.
Ensuite, examen de la question de la durée de vie des références. Quelques retouches à faire : un bout de texte qui parle de l'espace occupé par une référence (une référence, techniquement, n'est pas un objet et n'occupe pas d'espace). Aussi, la question de la fin de la vie d'une référence, ce qui peut jouer dans certains cas limites comme la fin d'un objet contenant des références : selon que le destructeur soit trivial ou non, l'ordre de destruction des membres n'offrira pas les mêmes garanties.
À quel moment passer par une référence menant vers un objet décédé devient-il un cas de comportement indéfini? On a des cas comme :
struct X
{
int i;
constexpr X(int i)
: i{i}
{
}
};
constexpr X x{3};
X &r = x;
new (static_cast<void*>(&x)) X{4}; // effet sur la vie de r?
On revient sur la terminologie des concepts, pour le passage définissant at least as constrained as qui posait problème selon l'analyse de Botond Ballo. On l'envoie à Andrew Sutton pour intégration au TS.
On a ensuite un cas d'ajustement terminologique à l'utilisation de type ODR-use d'une fonction virtuelle qui serait =delete, pour éviter qu'un ODR-use implicite soit une référence à cette fonction et fasse échouer la compilation. On constate au passage qu'il faut réserver un espace dans la vtbl pour la fonction même si elle est supprimée.
Découverte personnelle : on peut faire =delete sur les opérateurs new et delete implémentés sous forme de méthodes!
Un dossier mineur mais amusant : clarifier le fait qu'atteindre la fin d'une fonction non-void sans retourner quelque chose est un comportement indéfini. La question qui tue est le traitement de main(), cas unique qui a un return implicite d'un code de succès mais est int; on s'en sort par la distinction entre reaching the end of main() et flowing off the end of a function.
Nous prenons une pause très brève; j'en profite pour clavarder avec ma belle Za et l'adorable Vayda.
Après quelques trucs pour alléger l'écriture de certains passages du standard sans altérer l'intention, on parle des cas de Copy Elision dans un une expression constexpr.
On a un cas pervers d'un objet avec invariants cachés, initialisé à la construction sur une base constexpr :
struct A
{
void *p;
constexpr A()
: p(this)
{
}
};
constexpr A a; // well-formed : a.p == (void*)&a
constexpr A b = A{}; // peut être mal formé sans Copy Elision: b.p peut pointer sur une temporaire
Amusant mais douloureux : on a un cas ou T a = T(); et T a{}; ne donnent pas nécessairement la même chose! Notre choix immédiat a été d'accepter ceci, mais plus tard dans l'après-midi (je fais un saut dans le temps ici), Gabriel Dos Reis et Daniel Krügler ont réagi par écrit en se disant surpris de notre position. C'est vrai que c'est un peu weird, mais Copy Elision est optionnel alors il vaut mieux ne pas écrire du code qui en dépende.
Richard Smith lance en quasi-boutade que Copy Elision est l'optimisation que nous faisons mais qui n'est pas as-if, donc qui a un effet observable sur la sémantique d'un programme.
On se demande s'il serait préférable de donner dans le « pas de Copy Elision dans un contexte constexpr », puisque le résultat de l'initialisation est résolu à la compilation de toute manière. On essaie de voir les conséquences, par exemple dans des expressions ternaires. Finalement, ma compréhension est que la copie sera obligatoire dans ce cas. Morale de cette histoire : ne pas écrire de code sémantiquement dépendant de Copy Elision.
Autre dossier complexe, celui de l'initialisation à zéro pour les variables statiques et les variables Thread-Local. Beaucoup de travail d'écriture ici. On remarque qu'une variable static locale à une fonction, si sa construction lève une exception, n'a pas été construite et la construction sera ré-essayée au prochain appel.
Pour les variables Thread-Local, on cherche une terminologie qui permettra l'efficacité maximale d'initialisation selon l'implémentation, car dans certains cas il est préférable d'initialiser à zéro au démarrage alors que dans d'autres cas, l'essentiel est de le faire avant la première utilisation.
On a des perversions possibles comme une fonction retournant par decltype(auto) une λ sans capture qui, pour sa part, retourne l'adresse d'une variable globale thread_local.
Que faire quand on charge une bibliothèque à liens dynamiques? On vise un as-if ici qui éliminera les différences observables entre initialisation statique et Thread-Local tout en permettant d'être efficaces selon les choix d'implémentation. Une des difficultés que nous avons est que les divers modes d'initialisation (dynamique, statique, Thread-Local) ne sont pas regroupés en un seul endroit, alors il faut s'éparpiller un peu et assurer une forme de cohérence.
Exemple absolument pervers de Richard Smith :
auto f() {
static int n = 123;
struct X { int &f() { return n; } };
return X();
}
int &r = decltype(f())().f();
Sans commentaire.
Enfin, on parle d'alignement, plus précisément de multiples clauses alignas et des conséquences de possibles demandes incorrectes, comme on pourrait l'avoir avec une expression variadique.
Le cas alignas(0), qui est présentement considéré « sans effet », pourrait être simplement not well-formed.
C'est pas simple de déterminer la bonne formule : si on a deux alignements et si l'un des deux est trop « mou » alors que l'autre est suffisamment strict, devrait-on ignorer le mou ou rejeter l'expression?
On arrête vers 17 h 25. Demain, si on ne reçoit pas trop de nouvelles propositions, on devrait pouvoir adresser des trucs prioritaires qui trainent depuis un bout.
Je suis allé me chercher une salade l'autre côté de la rue. J'espère que les reçus par courriel fonctionneront cette fois, sinon j'espère que mes relevées de carte de crédit me permettront de faire mes réclamations.
Après avoir mis un terme aux travaux du CWG, je suis allé jeter un coup d'oeil à la fin des travaux du EWG, juste à temps pour voir un membre de l'audience fustiger, pour ne pas dire planter, le comité dans son ensemble pour ne pas arriver à choisir entre plusieurs approches pour les fonctions « résumables ». C'était rude, c'est le moins qu'on puisse dire, malgré un ton calme et une absence de nominatif.
Je suis un peu étourdi; je vais essayer de me coucher plus tôt ce soir, après la séance de ce soir sur la réflexivité. Je suis un peu surpris, mais le sujet n'accroche pas tout le monde, alors nous serons probablement moins nombreux que nous ne l'étions hier soir.
La session plénière de ce soir porte sur la réflexivité, sujet qui m'intéresse beaucoup. Entre-temps, je pique une petite jasette avec Hubert Tong, qui est franchement sympathique.
La rencontre de ce soir est dirigée par Chandler Carruth. On commence vers 19 h 40.
On examine d'abord Parameter Stringization, par Robert Douglas.
http://www.open-std.org/jtc1/sc22/wg21/docs/papers/2015/n4418.pdf
On parle de réflexivité, d'une manière visible à même la signature des fonctions. Robert Douglas utilise @ pour qualifier ses paramètres (capture à la pièce), mais c'est à titre indicatif. Il qualifie ses fonctions par reflect entre la parenthèse fermante et l'accolade ouvrante. Un truc comme
template <class T, class U>
void f(T t, U u) reflect
{
auto s = f.param<0>().as_string();
}
...ferait de s une version string du paramètre t. On s'inspire clairement des std::tuple ici.
Robert Douglas parle aussi d'un service source_context() qui donnerait accès aux numéros de lignes, au nom de la fonction, ce genre de truc. Il souhaite éliminer les macros comme __FILE__ ou __FUNCTION__.
Une autre variante serait d'ajouter un paramètre avec valeur par défaut à la signature des fonctions (un std ::reflect_stuff, genre).
On a plusieurs questions ouvertes. Entre autres, la proposition propose de « stringifier » les éléments mais on ne voit pas clairement comment utiliser d'autres types que std::string pour y arriver.
Pendant les travaux, Hubert Tong travaille sur une résolution pour le problème 1990 dans un cas où la grammaire de C++ est ambiguë pour les constructeurs; on examine le choix de formulation. Ce qu'il propose me semble pas si mal. Il est`très habile sur ce point. On échange aussi un peu sur la politique de WG14, le comité de standardisation du langage C, qui est une bête bien, bien différente de WG21.
Le recours à @ ne passe pas bien. J'entends don't mess with the types dans la salle. L'approche qui prend en paramètre un reflect_stuff semble plus flexible à mes yeux. C'est bien reçu, mais un membre dans l'assistance est fortement contre (la possibilité de surcharger le paramètre lui semble une aberration); ce membre préfère la version avec mot clé reflect qualifiant la fonction.
On examine ensuite Source-Information Captures Extensions, par Robert Douglas.
http://www.open-std.org/jtc1/sc22/wg21/docs/papers/2015/n4419.pdf
Cette proposition amène un objet représentant un point dans un fichier, pour remplacer les macros. Évidemment, on parle de trucs constexpr ici.
Truc amusant : on a voté, c'était relativement favorable, mais Chandler Carruth a mis son chapeau de « gars de compilateur » pour le vote et a dit que ça ralentirait le compilateur et qu'il se battra jusqu'à la fin pour empêcher ceci d'être intégré au standard. Ensuite, il a encouragé le présentateur à continuer... Ah, la vie à WG21!
Il y a de la résistance semblable à d'autres volets de la proposition. Quelqu'un soulève la question du support optionnel de cette mécanique : si on ne veut pas ralentir la compilation, alors n'utilisons pas ces facilités, tout simplement. Ça semble raisonnable, mais Chandler Carruth indique que l'on manque d'expérience concrète pour avoir la capacité de poser un jugement raisonnable.
Alisdair Meredith pense aussi que l'idée est chouette mais que des données empiriques sont requises pour être en mesure de poser un jugement raisonnable. Chandler Carruth dit que pour faire de vrais bons tests, faudrait créer des fichiers pathologiquement méchants et voir ce que ça donne.
On y reviendra quand on aura des données et quand on sera plus en mesure de juger des conséquences.
Ensuite, Defining Test Code, par Robert Douglas et Michael Price
http://www.open-std.org/jtc1/sc22/wg21/docs/papers/2015/n4420.pdf
Comment marquer le code qui ne devrait servir qu'à fins de test? Pourrait-on le faire par des attributs ou des mots clés plutôt que par des macros? Leur syntaxe est :
int main() test
{
f();
}
...qui appellerait void f() test; mais pas void f();. Ils parlent de code « atteignable » ou non à partir d'un certain point. On ne peut pas utiliser des annotations ici car celles-ci ne sont supportées que de manière optionnelle.
Une fonction marquée test pourrait appeler une fonction non-test mais pas l'inverse.
J'ai de grosses réserves ici; je crains que ça ne dérape en spaghetti de cas particuliers.
Chandler Carruth fait un rappel à l'ordre, au sens où on est un peu hors-sujet dans un forum sur la réflexivité. En généralisant le propos, par contre, c'est autre chose. Faudra voir.
On examine alors Static Reflection, par Matus Chochlik (il me manque des accents ici), présenté par un tiers qui en aime le contenu.
http://www.open-std.org/jtc1/sc22/wg21/docs/papers/2015/n4451.pdf
C'est un gros document, ≈75 pages, mais qui peut être segmenté et adopté de manière modulaire.
La clé : l'opérateur statique mirrored(T) retourne un type « sans nom » qui décrit T, puis des traits sont applicables sur ce type pour raisonner à son sujet dès la compilation. Ces traits décrivent les « concepts » implémentés par T (les « concepts » sont entre guillemets parce que c'est le nom choisi par l'auteur, mais le présentateur n'est pas convaincu que l'on parle ici de la même chose que l'idée de concept tel qu'envisagé pour C++ 17). Par exemple, si mirrored(N::C) est T, alors meta::scope<T>::type est N car C est un sous-type de N.
Chandler Carruth indique que mirrored(T), tel qu'exprimé, pourrait fonctionner sur des templates et sur des types incomplets. Quelques éléments ont été enlevés de la proposition générale pour qu'on puisse digérer ce que l'auteur met de l'avant. On aura, avec cette proposition, des trucs comme la liste des constantes énumérées d'un type et autres trucs sympas. Cette proposition couvre aussi le cas des espaces nommés.
Chandler Carruth dit que la proposition originale comprend aussi un opérateur reified(U) pour complémenter mirrored(T) de telle sorte que (essentiellement) std::is_same<reified(mirrored(T)),T>::value s'avère.
Pouvoir faire de la sérialisation sur la base de la réflexivité semble être une préoccupation pour bien des gens. Le vote met en relief que les membres du comité souhaitent pouvoir faire de la réflexivité sur des typedef. La capacité de réflexivité sur des templates et leurs caractéristiques sans nécessairement les instancier a aussi attiré un intérêt, mais moindre. Chandler Carruth est contre car il ne souhaite pas permettre d'observer l'ordre d'instanciation des templates. La notation sur la base d'opérateurs ne rencontre pas de résistance.
Enfin, on a From a type T, gather members name and type information, via variadic template expansion, par Cleiton Santoia Silva.
http://www.open-std.org/jtc1/sc22/wg21/docs/papers/2015/n4447.pdf
Le pauvre Cleiton Santoia Silva n'a que dix minutes pour presenter... Il propose :
C'est flexible! Ça permet quelque chose comme :
auto s = { typedef<X,is_setter> }; // initializer_list!
On n'a pas donné à Cleiton le temps requis pour exprimer ses idées. Heureusement, son approche basée sur des foncteurs statiques a été bien reçue. Certains (Hubert Tong par exemple) favorisent une approche plus « langage », mais moi j'aime bien la vision métaprogrammée que Cleiton met de l'avant.
On se sauve en courant pour prendre l'autobus, qui quitte à 21 h 35.
Dans l'autobus, on prend un peu de temps pour rassurer Cleiton Santoia Silva. Botond Ballo me raconte sa journée mouvementée (modules, coroutines), je m'amuse en lui racontant les hauts et les bas des trucs étranges couverts dans CWG.
Enfin à ma chambre d'hôtel, je souhaite bonne nuit à mon amoureuse adorée.
Petite jasette avec Thomas Köppe, puis Lawrence Crowl (qui nous a raconté une anecdote à propos de CADET, mais faudra me la demander en personne si vous voulez des détails) et Cleiton Santoia Silva, qui faisait de la bioinformatique durant sa maîtrise.
On commence vers 8 h 45
Retour par un des membres sur des échanges hier soir portant sur la terminologie pour les Feature Test Macros (un court texte disant que ça existe, puis un table nom/valeur).
Petit retour sur une retouche terminologique en lien avec ces macros. Ça va revenir à plusieurs TS qui seront votés vendredi. On va aussi recommander de pousser le TS de mémoire transactionnelle vendredi sur lequel on pourra voter.
Hubert Tong revient avec une formulation pour le problème dont nous avions discuté hier soir lui et moi et portant sur une ambiguïté grammaticale dans certaines utilisations de constructeurs. On travaille fort pour éviter que les changements grammaticaux proposés de permettent pas d'écrire des aberrations et ne fassent qu'atteindre leurs objectifs. On ne termine pas sur-le-champ (c'est subtil).
On revient sur la question de Copy Elision dans un contexte constexpr. Hubert Tong fait remarquer que la question s'applique aussi à l'initialisation de variables statiques. Richard Smith fait remarquer que dans une expression complexe, il pourrait arriver que l'on ait un traitement où les décisions prises antérieurement sur l'élision ou non soient renversées par l'interdit de réaliser l'élision pour des raisons contextuelles. Richard Smith mentionne aussi que certaines implémentations garantissent Copy Elision dans certaines circonstances; nous risquons de les briser ici.
Je fais remarquer que le débat ressemble beaucoup à celui des bris d'invariants cachés lors de mouvement implicite; Jonathan Caves dit que c'est de là que move_if_noexcept() était ressorti.
On se demande si on peut garantir la portabilité des sources dans une telle situation. Richard Smith rappelle que Copy Elision n'est pas as-if. Hubert Tong demande si on peut dire que le comportement est Implementation-Defined alors. Richard Smith estime que ce serait raisonnable. Jens Maurer dit qu'on pourrait demander à Gabriel Dos Reis s'il résiste toujours à notre position. Hubert Tong dit que plusieurs personnes écrivent du code qui n'a de sens qu'avec Copy Elision. Jens Maurer dit que c'est pas portable. Richard Smith dit que ça arrive quand même.
On fait remarquer que les calculs statiques ne pourront plus être garantis comme donnant des résultats identiques aux calculs dynamiques, mais Jens Maurer rappelle qu'on ne le fait déjà pas pour les nombres à virgule flottante. Je penche pour Implementation-Defined : c'est pas portable, mais si t'as un seul compilateur dans ta vie, et si t'es à l'aise avec ses choix bien spécifiques, c'est ta vie.
On va y revenir, c'est pas réglé encore...
On revient sur la question de l'initialisation à zéro des variables static et thread_local. Jens Maurer a retravaillé la formulation. Le texte ratisse plus large et couvre l'initialisation statique des variables non-automatiques en général.
Hubert Tong demande si on devrait discuter des temporaires.
On travaille sur la formulation, particulièrement dans le cas des thread_local, pour être certains de dire ce qui doit vraiment être dit. Faire une bêtise ici changerait l'ordre d'initialisation des variables!
On examine aussi certains déplacements entre sections pour faciliter la compréhension, mais faut suivre de près les références croisées. On finit avec quelque chose de pas mal.
Vient ensuite sur la table un truc pas cool, soit que && est parfois valide dans un nom de type alors que ce ne l'était pas en C++ 03 (à cause des références sur des rvalue), ce qui permet de briser du code horrible comme :
bool b1 = new int && false; // previously false, now ill-formed
struct S { operator int(); };
bool b2 = &S::operator int && false; // previously false, now ill-formed
Oh boy... On ajoute une note dans une annexe de bris de compatibilité.
On parle ensuite de conversions contextuelles et du mot explicit. La terminologie dans un cas précis semble permettre d'appliquer une conversion explicit sur la base du contexte mais sans expliciter l'intention. On se demande si on devrait contraindre un peu plus et éliminer les conversions explicites du lot. On est pas mal d'accord.
Ensuite, on a une « simple » retouche pour dire qu'une λ non-générique sans capture convertie en fonction à l'aide d'un opérateur de conversion noexcept(true). On retouche le texte pour fins de conformité sans changer l'intention, mais faut valider les changements.
Brève pause vers 10 h 15. Stephan T. Lavavej vient nous voir avec un bogue étrange sur std::function :
struct X {};
struct Y { operator X() const; };
Y inner();
const X& outer() { return inner(); }
Ça compile mais c'est sale parce que la temporaire s'évapore si outer() est en fait une std::function. Le problème est la génération d'une temporaire. Stephan T. Lavavej se demande si un trait comme is_safely_returnable serait nécessaire. Hubert Tong dit qu'on a un truc terminologique sur les bindings aux temporaires mais n'est pas sûr que ça fasse l'affaire.
C'est subtil car un faux-mouvement ici briserait le traits is_convertible. On a un problème pour les bindings indirects (les bindings directs sont Ok). Hubert Tong indique que ça touche aux séquences de conversion implicite sur lesquelles on travaillait hier. Cela dit, on n'a pas de solution immédiate (c'est un peu sur-le-vif, faudra y réfléchir).
Après la pause, on revient sur le fait que les fonctions de conversion en fonctions globales de λ non-génériques stateless devraient être non-throwing. Le cas des λ génériques est maintenant aussi couvert de manière analogue.
Décrire une fonction qui retourne une fonction et parler des particularités de chacune sans écrire de phrase ambiguë est un exercice de style complexe. On sépare en plus petites phrases.
On se demande si on devrait rende ces fonctions de conversion constexpr aussi. À voir; on en discutera avec EWG qui a un sujet semblable sur la table.
Ça finit par faire un texte complexe. L'air de rien, on a travaillé longtemps là-dessus.
Vient 1391 et la conversion de types de paramètres dans des arguments non-déduits. La formulation recherchée doit tenir compte des noms dépendants de types et du moment où l'on sait ce que ces noms signifient. Richard Smith questionne les moments dans le processus de compilation où certaines transformations et vérifications devront être faites. La proposition est bien faite et l'exemple est utile.
On revient aussi à nouveau sur l'ambiguïté grammaticale pour les constructeurs. La relecture est complexe (il y a une dizaine de clauses grammaticales en jeu). On simplifie un peu les libellés. Faut ensuite s'assurer que les nodeclspec-function-declaration n'apparaissent pas là où elles ne sont pas à propos (donc à quelques endroits seulement, dont les templates qui sont une grammaire à part entière). Hubert Tong a travaillé fort.
Un item est proposé par James Widman pour la syntaxe des initialisations entre accolades. La terminologie init-operand est mal reçue, alors on retravaille la terminologie pour en arriver à un truc comme expr-or-braced-init-list. Faut s'assurer que le tout s'intègre grammaticalement aussi dans les initialisations de répétitives for sur des intervalles. Richard Smith rapporte qu'on a un autre cas grammaticalement suspect à corriger. James Widman va prendre la balle au bond et poursuivre les travaux puisque l'idée est bien reçue.
Autre item proposé par James Widman et portant sur la définition des méthodes constexpr. On examine des considérations d'analyse en deux temps de certaines structures grammaticales, et une formalisation plus claire des déclarations a priori et des évaluations a posteriori. Ça touche des cas étranges comme :
struct X
{
static constexpr std::size_t size()
{
return sizeof(X); // X is complete type
}
char arr[size()]; // is 'size' undefined here?
};
struct Y
{
static constexpr int offset()
{
return offsetof(Y, c); // Y is complete type
}
char arr[offset()]; // is 'offset' undefined here?
char c;
};
struct Z
{
static constexpr int f()
{ return g(); } // is 'g' defined here?
static constexpr int g()
{ return 0; }
};
Hubert Tong craint qu'une même entité puisse être une déclaration a priori et ne pas l'être en même temps. Richard Smith fait remarquer que les valeurs par défaut des paramètres sont aussi des évaluations a posteriori.
Il semble qu'un manque de clarté sur ces points ait compliqué la vie de bien des gens. Ceci impact entre autres notre capacité d'appeler une méthode constexpr pour obtenir la taille d'un tableau à la compilation. Jens Maurer conteste le recours à forward-declared ici, qui ne sert pas vraiment dans le reste de la proposition; on trouve une alternative.
Hubert Tong demande comment évaluer la fin d'une région de code sans en analyser le contenu; semble qu'on y arrive en examinant la correspondance des accolades et des chevrons. On ne veut pas non plus casser le code C++ 98 avec l'écriture ici qui dicte l'ordre d'évaluation des trucs. En retour, on pourrait en arriver à une écriture par laquelle les classes et les class templates sont traitées de manière plus semblable. Jens Maurer recommande qu'on passe cette partie à EWG, car c'est substantiellement plus complexe.
On n'a pas de résolution, outre que l'ordonnancement restera non-spécifié. Une autre approche serait d'introduire des éléments grammaticaux indiquant où les choses se terminent, mais ça fait peur aux gens de compilateurs dans la salle (pour les trois principaux compilateurs).
À suivre...
On s'arrête vers midi. Jens Maurer prend un peu de temps pour me donner des pistes contextuelles pour mieux participer aux débats (c'est gentil; j'arrive à tout suivre et à participer mais si j'étais mieux préparé, ma contribution serait plus intéressante). Je prends quelques minutes pour manger une salade (mioum!) et m'amuser des changements politiques en Alberta hier (radical!).
À la fin de l'heure du dîner, Jens Maurer revient et nous informe qu'il est possible que nous puissions traiter les coroutines proposées par Gor Nishanov cette semaine, du fait que ses travaux semblent avoir obtenu l'assentiment du EWG, modulo peut-être une question de choix de mots clés. Quelques secondes plus tard, Bjarne Stroustrup est venu nous informer avoir reçu un courriel d'Andrew Sutton indiquant qu'il a implémenté et testé les contrained auto. Ça, c'est franchement chouette!
Jens Maurer nous informe que Gor Nishanov risque de pouvoir amener les couroutines à CWG d'ici la fin de la semaine.
On examine une demande qui cherche à clarifier le type de p dans :
void f(int a[10], decltype(a) *p);
... à savoir s'il devrait s'agit de int(*)[10] ou de int**.
Hubert Tong fait remarquer que la terminologie proposée, qui parle entre autres de type de retour, n'aide pas à clarifier la définition des constructeurs génériques. On ajoute des précisions. C'est une retouche importante, qui touche le nommage des paramètres dans les templates, alors ça prend du temps et du doigté.
On a un échange par la suite sur le fait qu'un exemple laisse entendre que sizeof(T) n'est pas équivalent à sizeof(T)+0, ce qui me laisse perplexe et lance une brève discussion sur la nuance entre « équivalent » et « fonctionnellement équivalent ». Cela rappelle des échanges avec Botond Ballo sur la nature de l'équivalence fonctionnelle dans les concepts, ces dernières semaines.
Truc agaçant : les trois principaux compilateurs traitent alignas(sizeof(T)) et alignas(sizeof(T)+0) comme étant équivalents, mais pas le standard. J'ai testé avec :
Le problème est que l'équivalence ne tient compte que des noms, donc les compilateurs ont tort (c'est parce que le standard indique no diagnostics required). Mais ça agace d'autres gens, alors on cherche un meilleur terme que « équivalent », qui a un sens technique ici, pour éviter les aberrations. Richard Smith suggère d'y aller avec une définition contextuelle de l'équivalence, pour prendre en charge les cas de généricité (simple, double, …).
Un gros 75 minutes de travail ici, et on n'a pas fini.
Suit, le cas de l'item 1863 quant à ce qu'on exige d'un objet pris en charge par std::current_exception(). Le libellé actuel laisse entendre que l'objet doive être copiable. On précise ce qui est considéré odr-used dans un tel cas (ce qui est sélectionné l'est, que ce soit utilisé ou élidé en pratique).
Hubert Tong soumet le problème 2020 pour le cas étrange du sous-objet de delta zéro dans les requis du standard layout, dans un cas d'EBCO. Sa formulation est acceptée.
James Widman soumet 1247, un correctif mineur (suppression d'un passage irritant). Ok.
Pause vers 15 h 30. Jens Maurer m'explique au passage (de manière humoristique) le système de priorités utilisé par CWG, et me souligne qu'on ne se rend essentiellement jamais aux cas de priorité de niveau 2 en pratique parce qu'il y a trop à faire :
Priorité | Signification |
---|---|
Niveau 0 |
Nous avons un bouc émissaire pour la rédaction, et nous savons ce que nous devons faire |
Niveau 1 |
Oh la la, nous ne savons pas quoi faire, nous avons besoin d'un bouc émissaire (typiquement, un problème pour lequel les solutions offertes par les diverses implémentations diffèrent, ce qui est très vilain) |
Niveau 2 |
Ce n'est pas terriblement important pour le moment, mais pourquoi ne pas s'en occuper si ça compte à tes yeux? |
Niveau 3 |
Euh... C'est gentil de nous en faire part (lire : oui, c'est un bogue, mais ça n'a pas de réel impact dans les implémentations alors ça n'ira pas de si tôt sur le dessus de la pile) |
Je suis passé bavarder avec Herb Sutter pour me faire ajouter aux listes d'envoi. Il m'a donné la procédure, que j'ai suivie.
Jens Maurer et Richard Smith nous quittent car les deux ont des propositions qui passent devant EWG.
On attaque les cas de priorité de niveau 1. Nous avons couvert plus de cas que ceux listés ici, mais certains ont simplement été mis sur la glace le temps que nous puissions consulter les autres des soumissions.
Item 1584 : déduire un type de fonction sur un type générique cv, p. ex. :
void f() {}
template <class T> deduce(const T*){}
int main()
{
deduce(f); // légal?
}
Ou encore ceci, qui provoque une récursion infinie :
template <typename T> struct tuple_size {};
template <typename T> struct tuple_size <T const>: tuple_size<T> {};
tuple_size <void()> t; // et c'est parti!!!
La résolution accompagnant le signalement du problème propose d'ignorer la qualification cv dans ce cas. L'avis du CWG est de l'interdire, carrément. C'est plus simple.
Item 1907 : ceci est-il bien formé?
void f(int, int);
template<typename T> void g(T t) { f(t); }
void f(int, int = 0);
void h() { g(0); }
C'est pas clair, évidemment. Si on se base sur les noms seulement, c'est Ok. On y reviendra.
Item 1928 : préciser ce qui est trivialement copiable. On a une opposition forte pour ce qui est de laisser l'accessibilité affecter la trivialité, comme le suggère https://isocpp.org/files/papers/n4148.html, mais si nous suivions les recommandations de cet article, nous aurions une trivialité selon le contexte d'utilisation, ce qui serait étrange. Hubert Tong craint que cela ne mène quelqu'un à essayer de profiter de « copiabilité triviale » des contextes inattendus. On va attendre pour celle-là aussi.
Item 1962 : le type de __func__ est-il un tableau de char ou un const char*? Le traitement varie selon les implémentations actuellement, même si le standard suggère static constexpr const char[]. Avec const char*, ça laisse moins de latitude pour faire varier les implémentations. De puis, si on traite __func__ comme un char[], alors passer __func__ à un truc comme
template<class T> void f(T&);
...donnera des résultats variant selon l'implémentation, et peut casser les ABI. Ça impacte 8.4.1 parag. 8.
On se demande si on devrait bloquer les gens qui veulent s'amuser à explorer __func__ avec des fonctions constexpr, et on décide de les laisser se faire plaisir.
Item 1965 : le plaisir de dynamic_cast et de static_cast sur des références entre parent et enfant. Présentement, le standard bloque un cas de static_cast d'une manière qui provoque une asymétrie discutable.
On s'interroge à savoir si on doit faire une manoeuvre semblable pour const_cast, on se demande pourquoi on le ferait, et Hubert Tong trouve un cas pas gentil qui rendrait ça utile.
Jens Maurer et Richard Smith reviennent : les constructeurs hérités réécrits par sont acceptés; intégrer noexcept au système de types aussi. Ils sont de bonne humeur.
Richard Smith indique qu'on peut implémenter, de manière surprenante, std::move() à l'aide de const_cast (à réfléchir).
On essaie d'éviter que const_cast ne puisse convertir un T&& en const T&.
Ce qui se passe quand T est un tableau est préoccupant : on ne veut pas que
const_cast crée des temporaires comme le fait parfois
static_cast. Le
correctif amené cet après-midi semble avoir été suggéré par Hubert Tong et Michael Wong... il y a quelques années
.
Item 1979 : un cas d'alias génériques...
template<typename T>
struct A
{
struct B
{
void f();
};
};
template<typename T> using X = typename A<T>::B;
template<typename T> void X<T>::f() { } // ceci définit-il A<T>::B::f()? Ça dépend de T!
Jens Maurer trouve qu'on pousse fort l'obfuscation ici. C'est faisable, à première vue, mais ça peut devenir complexe si on considère des cas moins banals. Jonathan Caves suggère de restreindre les usages aux cas simples (raccourci pour un type de retour de fonction, par exemple) mais n'est pas sûr comment l'exprimer.
On a une résolution connexe pour l'item 1286, dont on peut s'inspirer.
Richard Smith fait remarquer qu'il est agaçant qu'une spécialisation partielle ait ici un meilleur traitement qu'une version générique. Faudra écrire un truc, ce que Jason Merrill va faire... mettons...
On ferme pour aujourd'hui. On aura de nouvelles propositions demain. Semble que LWG ait besoin d'aide ce soir, alors on va aller y faire un tour.
On travaille sur des bogues de la bibliothèque standard.
Dans sa version actuelle, raw_storage_iterator fait des copies et ne supporte pas le mouvement, ce qui bloque certaines manipulations de types incopiables. On propose d'ajouter une surcharge de operator=(T&&) qui ferait un placement new. Stephan T. Lavavej signale qu'il y a un piège ici car placement new n'a pas l'exigence que l'objet construit soit identique à l'objet d'origine. Il ajoute que ce serait un bon moment pour ajouter une fonctionnalité qui fait un relais parfait d'un U&&, mais constate ensuite que ça casserait le code existant. Je demande si ça peut causer des bris silencieux, qui exigerait une sorte de move_if_noexcept, mais Stephan T. Lavavej et Howard Hinnant semblent passer que ce n'est pas un risque (ça devrait être inobservable). Stephan T. Lavavej insiste sur le fait que personne n'utilise vraiment raw_storage_iterator dans la bibliothèque standard. Ok.
La formulation de get_temporary_buffer() est confuse, en particulier dans sa version japonaise, quant à la quantité de mémoire qui sera effectivement allouée (surtout pour le minimum; la fonction peut retourner moins que ce qui lui est demandé). Hubert Tong demande comment cette fonction interagit avec les autres mécanismes d'allocation de mémoire. Plusieurs gens ne sont pas convaincus du rôle de cette fonction; je présume qu'elle n'est que peu utilisée. Ok.
La fonction addressof() devrait être constexpr. Jonathan Wakely fait remarquer que cette fonction requiert reinterpret_cast, donc faudra du Compiler Magic pour qu'elle soit constexpr. Stephan T. Lavavej est d'accord, mais c'est de la magie simple à réaliser. Jonathan Wakely suggère qu'on consulte les implémenteurs des compilateurs avant de trancher de notre côté. On va dans cette direction.
Rien ne dit que unique_ptr<T>::get_deleter()(p) doive être capable de détruire *p, mais rien ne dit non plus que le unique_ptr<T> n'utilisera pas *p par la suite. Un libellé est proposé qui propose un discours sur la mort du deleter. On jase de choix terminologiques. Ça ne semble pas un dossier assez « mature » pour être traité ce soir.
Faudrait ajouter des spécialisation de std::hash pour les types entiers étendus, par exemple les entiers sur 128 bits. On s'entend pour Ok.
Il manque une contrainte SFINAE de haut niveau. Ça revient à plusieurs à plusieurs endroits dans le standard, c'est répétitif, mais faudrait mieux faire. On pourrait alors parler de « [telle ou telle expression est] valid in contrived argument deduction context for types [T, U, ...] ». La difficulté est de bien définir « valid in contrived argument deduction context for... ». On met du monde là-dessus.
Les types std::pair et std::tuple ne sont pas correctement écrits pour std::is_constructible dans le cas sans paramètres. Howard Hinnant explique par un exemple :
struct X
{
X() = delete;
};
int main()
{
using P = std::pair<int, X>;
static_assert(!std::is_constructible<P>::value, "");
static_assert(!std::is_default_constructible<P>::value, "");
using T = std::tuple<int, X>;
static_assert(!std::is_constructible<T>::value, "");
static_assert(!std::is_default_constructible<T>::value, "");
}
// For me these static_asserts fail. And worse than that, even asking the
// question fails (as opposed to gets the wrong answer):
asseassert(!std::is_constructible<P>::value);
La proposition est un tour de passe-passe avec enable_if. On en profite pour demander de zapper quelques conseils sur l'implémentation, mais ça alourdit le processus alors on laisse faire. Jonathan Wakely dit souhaiter très fort un nouveau feature discuté dans une rencontre antérieure qui générerait les constructeurs au besoin s'ils ne sont pas explicitement supprimés pour des agrégats « simples » pour std::pair ou std::tuple.
Il semble que les traits is_trivially_constructible et is_trivially_assignable soient toujours faux. C'est un problème de terminologie, la prescription dans le standard étant incorrecte. On en profite pour faire une retouche pour clarifier le fait que declval<T>() ne soit pas odr-used. Ok.
Certaines transformations peuvent mener à des types impossibles. Alisdair Meredith dit qu'on a probablement déjà réglé ceci par une reformulation de CWG sur les types referencible. Marshall Clow pense que non. Ville Voutilainen dit l'avoir vu ailleurs. La résolution semble Ok. Go.
Bavardage sympathique mais bref avec Botond Ballo, Roger Orr et Stephan T. Lavavej sur le chemin du retour. Il fait noir et on est tous brûlés. Derrière moi, quelqu'un ronfle. De retour à l'hôtel, j'essaie de dire à mon amoureuse que je l'aime et qu'elle me manque, puis dodo...
Ce matin, je me suis pris un café et un yogourt au Starbucks de l'hôtel. La bonne nouvelle : j'ai pensé prendre un reçu cette fois. La moins bonne nouvelle : pour un café et un yogourt, Starbucks demande... 7,05$, rien de moins. Ouf!
J'ai bavardé avec Cleiton Santoia Silva dans l'autobus, de manifestations étudiantes et autres. Sur place, des burritos matin avec salsa douce, et des fruits. C'est correct. Tout le monde est ici pour travailler, alors les déjeuners sont sympathiques mais courts.
Semble qu'on n'aura pas nécessairement les coroutines ce matin. Jens Maurer et Gor Nishanov ont travaillé jusqu'à minuit hier sur la proposition et on a quelques trucs liés aux mots clés (yield? coreturn? co_return? Ce genre de truc) à valider. Faut voir ce qui est harmonieux, mais aussi ce qui cassera le moins de code existant (le mot yield, en particulier, en est un qu'on suspecte populaire dans la pratique alors on risque de nuire aux gens si on le réserve).
Item : James Widman a une nouvelle formulation pour les initialisations avec accolades. Il y a des subtilités grammaticales sur le rôle des virgules, en particulier dans le cas des répétitives for sur des intervalles, mais Jonathan Caves explique que c'est pour éviter des ambiguïtés dans certaines manipulations naïves de gens qui passeraient à ce modèle à partir de répétitives for traditionnelles. C'est bien fait, Ok.
N4429 : au lieu de synthétiser des constructeurs chez l'enfant, on cherche maintenant à rendre les constructeurs du parent accessibles au name lookup. Ça enlève plein de bizarreries. Un changement subtil est que using dans une classe introduit maintenant un ensemble de déclarations plutôt qu'un nom, car un constructeur, au sens de C++, n'a pas de nom.
Jonathan Caves fait remarquer que c'est beaucoup mieux qu'avant, mais qu'il semble toujours y avoir une dichotomie entre les constructeurs et les autres membres. Richard Smith souligne qu'il n'est pas arrivé à les amener plus près l'un de l'autre, les méthodes de lookup étant différentes. Ce sera plus rapide aussi, car ça enlèvera des étapes intermédiaires de construction (et des risques de copies).
On a des retouches à faire sur les règles d'accès des constructeurs du parent dans la classe dérivée. La gestion des fonctions « spéciales » (copie, mouvement, destructeur) entre enfant et parent demande beaucoup, beaucoup d'attention. On y met pas mal de temps, c'est bien mais il reste des retouches à faire et c'est plein de pièges. Faut l'équilibre entre « dire quoi faire, pas comment » et « spécifier l'ordre dans lequel les opérations surviennent » pour qu'il soit possible d'écrire du code sémantiquement portable (p. ex. : l'ordre dans lequel les paramètres avec valeurs par défaut sont construits dans ce cas-ci).
On jase technique pour la gestion de la pile et la réduction des copies. C'est amusant d'être dans la salle avec les principaux auteurs de compilateurs et discuter technique dans ces cas extrêmes. Le changement proposé a un effet visible au sens où :
struct Trace
{
int n = 0;
Trace() = default;
Trace(const Trace &other) : n { other.n + 1 } {}
};
struct A
{
A(Trace tr = {}) { cout << tr.n << endl; }
};
struct B { using A::A; };
struct C { using B::B; };
C c; // avant, aurait affiché 2; maintenant, affichera 1
Pause vers 10 h 15. Hubert Tong fait remarquer qu'on a un cas étrange où il n'y aura plus de constructeur principal (le constructeur débutant la séance d'appels de constructeurs) mais où on aura une séquence de construction héritée.
On a une boîte de biscuits au chocolat ce matin, ce qui est chouette (mais engraissant).
On ne discutera pas l'article de Jens Maurer tout de suite sur l'intégration de noexcept au système de types car il doit s'absenter. On va plutôt traiter des problèmes à la pièce.
Le cas suivant est mal formé parce que la liste d'initialisation d'une référence ne tient pas compte des fonctions de conversion :
struct D{}; struct S { operator struct D &(); } s;
// D &d{ s }; // incorrect, mais discutable
D &dd = s; // Ok
D ddd = dd ; // ok
AvAvec une ref-vers-const, on créerait une temporaire et ça passerait, mais la création d'une temporaire est un cas discutable (pourquoi, après tout?). C'est probablement un oubli dans le cas des listes d'initialisation. Jason Merrill suggère d'utiliser des règles de 8.5.3 ici. Richard Smith dit oui, mais en escamotant le cas de la génération de temporaires. Ok.
On escamote certaines qualifications lors de certaines conversions :
template<typename T> T make();struct B {}; struct D : B {};
const int *p1 = make<int*>(); // ok, qualification conversion
const int *const *p2 = make<int**>(); // ok, qualification conversion
const int **p3 = make<int**>(); // error, not type safe
const int &r1 = make<int&>(); // ok, binds directly
const int *const &r2 = make<int*&>(); // weird, binds to a temporary
const int *&r3 = make<int*&>(); // error
const int &&x1 = make<int&&>(); // ok, binds directly
const int *const &&x2 = make<int*&&>(); // weird, binds to a temporary
coconst int *&&x3 = make<int*&&>(); // weird, binds to a temporary
C'est pas nécessairement gentil. La question est de déterminer ce qui serait un comportement raisonnable. Pas clair.
Cas étrange : le ternaire ne réalise pas les mêmes séquences de conversion pour les pointeurs et pour les références. On passe pour le moment.
Cas de types dépendants et Parameter Packs. Par exemple :
template<typename ...Ts> struct X { X(int); };
template<typename T> using Y = int;
template<typename ...Ts> void f() {
X<Y<Ts>...> x;
}
L'ellipse devrait mener à un type dépendant, mais c'est pas écrit. L'erreur est que X<int...> n'a pas de constructeur par défaut. C'est relativement simple à régler (un petit ajout de texte pour tenir compte du cas du Pack Expansion)
Questionnement à savoir si le code ci-dessous devrait être valide. Probablement pas, mais les implémentations divergent :
template<typename ...T>
struct X
{
void f();
static int n;
};
template<typename T, typename U>
using A = T;
template<typename ...T>
void X<A<T, decltype(sizeof(T))>...>::f() {}
template<typename ...T>
void X<A<T, decltype(sizeof(T))>...>::n = 0;
void g()
{
X<void>().f();
X<void>::n = 1;
}
Semble que ce soit un duplicat d'un cas déjà traité.
On a un échange ensuite sur une résolution avec laquelle Hubert Tong est en désaccord, sur le plan de la terminologie de valeur en termes de classes et de leurs attributs. On discute de la nuance entre valeur et (rvalue, glvalue, xvalue, etc.), le premier ne s'appliquant qu'aux scalaires et le second étant applicables aux entités de class type qui sont en fait des expressions.
Hubert Tong est d'avis que la valeur d'une classe peut être modifiée par la modification de la valeur d'un de ses attributs, alors que Richard Smith est d'avis que cette terminologie devrait (outre pour les union) se limiter aux scalaires.
La question des bitfields est soulevée : ont-ils un corresponding object?
Hubert Tong fait référence à 3.1 pour indiquer que les objets trivialement copiables ont une valeur. Il met en relief un tableau d'éléments contenant en premier attribut un int, dans quel cas accéder à un élément du tableau par un int* permettrait de modifier un int. On cherche une terminologie simplifiant 3.10 mais permettant clairement d'exprimer que modifier directement un sous-objet modifie aussi l'objet le contenant.
Hubert Tong suggère qu'on laisse le WG14 (langage C) y jeter un coup d'œil avant que l'on n'y touche, puisque cela pourrait affecter la compatibilité entre les langages, mais Richard Smith pense que C, plus laxe ici, pourrait raisonnablement choisir de traiter le dossier autrement. On ajoutera un passage de clarification à ce qui est proposé.
On se demande ensuite se ceci mène à un comportement indéfini :
int f()
{
union U { double d; } u1, u2;
(int&)u1.d = 1;
u2 = u1;
return (int&)u2.d;
}
Hubert Tong pense que l'affectation à la deuxième ligne devrait être illégale. Richard Smith dit qu'une passe comme ça est un cas de memcpy(). Jason Merrill trouve ça insatisfaisant dû à une copie redondante. Richard Smith dit que « memcpy() est spécial »; faut pas regarder trop creux sous la couverture.
On se demande si reinterpret_cast ne devrait pas être défini en ces termes. On est très près d'un truc comme aligned_storage ici… Adopter le document sur std::launder() fermera ceci.
Jens Maurer revient alors on aborde son texte proposant d'intégrer les spécifications d'exceptions au système de types. On pense entre autres aux pointeurs de fonctions ici, soit codifier le comportement attendu lors d'une copie d'un pointeur de fonction noexcept(false) dans un pointeur de fonction noexcept(true) par exemple.
Se préoccupe-t-on de noexcept seulement ou traite-t-on aussi des clauses throw()? Les clauses throw() sont transformées en une forme noexcept appropriée pour les besoins des conversions. La formulation générale suit celle utilisée pour les fonctions transaction_safe et celles qui ne le sont pas.
Pause vers midi. Je passe brièvement près du EWG où Bjarne Stroustrup présente entre autres des applications de la surcharge de l'opérateur « . », comme dans (je refais son exemple de mémoire) :
template <class T>
class Ref // faudrait gérer le reste de la Sainte-Trinité
{
T *p;
public:
Re() : p { new T }
{ }
Ref(T &&val) : p { new T{val} }
{ }
T& operator.()
{
// ... peu importe ...
return *p;
}
Ref& operator=(T && obj)
{
delete p;
p = new T{obj};
return *this;
}
~Ref()
{
delete p;
}
};
struct X
{
void f() { /* ... */ }
};
int main()
{
Ref<X> ref;
ref.f(); // bingo!
}
C'est pas un truc qui est dans la poche : il y a un vote, favorable, mais un « contre » et un « fortement contre ».
C'est le déluge dehors. Je traverse pour me prendre une salade, et ça me fait remarquer que le bâtiment a des indications interdisant d'amener des armes à feu. Eh ben...
Les travaux reprennent vers 13 h 30. Un gros après-midi s'annonce, où on doit examiner plusieurs dossiers soumis à notre attention pour les classer en ordre de priorité.
On poursuit les travaux entrepris avant le dîner sur l'intégration des spécifications d'exceptions au système de types. On vérifie l'interaction de noexcept et du polymorphisme dynamique (on ne peut pas devenir plus laxe en spécialisant). On vérifie aussi l'interaction des spécialisations de templates et de noexcept (oui, ça se fait). On ne peut par contre pas faire de surcharge sur la seule base de la présence ou non de noexcept.
On passe ensuite au classement des dossiers par priorité. On indique au passage qu'il est probable qu'une téléconférence survienne entre maintenant et Kona, impliquant CWG et EWG, pour régler des « issues » du EWG qui est débordé.
Item : permettra-t-on d'appliquer reinterpret_cast entre deux types de même taille et de même alignement? On l'envoie à EWG (on cherche ensuite l'endroit où on indique que lire d'un int non-initialisé est un comportement indéfini, et on ne le trouve pas...)
Item : Template default arguments and deduction failure. C'était passé de CWG à EWG et ça revient à CWG. On parle de cas comme ceci :
template<typename T> struct Wrapper;
template<typename T = int> void f(Wrapper<T>*);
void g()
{
f(0);
}
Ceci ne déduit pas T alors on présume int, mais c'est pas universel alors faut préciser le texte. On a un autre item, qui est en bonne partie connexe :
template <class T = int> void foo(T*);
void test()
{
foo(0); // #1 valide?
foo<>(0); // #2 valide?
}
Ici, test() est rejeté par la plupart des compilateurs. On va essayer de résoudre les deux. Jason Merrill indique qu'il faut continuer d'essayer de faire la déduction même si ça échoue. John Spicer est d'avis qu'on devrait avoir un statu quo, et que les deux foo de test() sont invalides, le paramètre m'ayant pas assez d'information pour permettre une déduction. Jason Merrill s'oppose à ce que ce code compile.
Item qui revient aussi d'EWG : on nous demande d'accepter des const float en tant qu'expressions constantes, mais on est conscients que ça pourrait briser les ABI. Ça fait peur parce que ça brise le code (et il existe une alternative : constexpr float!). On penche pour NAD, parce qu'on ne se voit pas faire chuter des avions du ciel ou des trucs semblables en brisant du code existant.
Item : élargir le concept de paramètres par défaut des templates aux variable templates. Ok, suffit d'ajouter deux mots à un texte existant.
Item : clarification des paramètres dans une spécialisation de templates (il y a une redondance). Ok.
Item : on nous demande de déprécier uncaught_exception() depuis qu'on a uncaught_exceptions(). Jens Maurer remarque que c'est déjà fait, donc NAD.
Item : il ya un problème de numéros de référence dans un exemple. Éditorial.
Item : on a un bout de grammaire qui est pas mal long pour parameters-and-qualifiers. Les correctifs faits par Jens Maurer tantôt aident. NAD.
Item : une question d'équivalence ou d'équivalence fonctionnelle, à savoir si on a un f() ou deux f() avec :
template<int N> struct A {};
template<short N> using B = A<N>;
template<int N> void f(B<N>) {} // #1
template<int N> void f(A<N>) {} // #2
On place ça dans priorité 1 mais ça recoupe les discussions d'hier.
Item : ajouter une note d'incompatibilité entre C++ 11 et C++ 14 pour :
int a = 1;
auto b{a}; // int maintenant, initializer_list<int> avant
Item : les cas de conversion à bool pour noexcept et static_assert sont trop laxes présentement (on pourrait considérer 0.5 comme un booléen, par exemple).
Item : le sens de ceci a changé avec les changements de grammaire :
auto f()->int(*)[4]; // function returning a pointer to array[4] of int
// not function returning array[4] of pointer to int
Item : cas de spécialisation de template et de namespace. Il y a un problème dans le texte quand on parle de spécialisation explicite de class template. Jens Maurer s'en occupe.
Item : clarification des spécifications d'exceptions pour les fonctions de déallocation (le standard est légèrement contradictoire). Est-on dans le comportement indéfini ou défini (p. ex. : appeler unexpected() ou terminate())? On hésite entre divers niveaux de priorité ou NAD puisque c'est un peu indéfini et un peu défini.... C'est un vrai problème, ce truc. Il se peut que je m'en mêle, question de me faire la main.
Item : questionnement sur l'interaction entre paramètres de templates, tableaux et décrépitude de pointeurs. Jason Merrill suggère de transformer l'écriture et, plutôt que de dire ce que n'est pas le paramètre, indiquer ce que c'est. Faudra voir.
Item : ce qui suit échoue si T est void :
T f();
decltype(auto) g() { return f(); }
Évidemment, faut arranger ça. C'est une mini retouche terminologique.
Item : la terminologie parle de paramètres « identiques » pour des templates mais on ne considère pas leurs noms. Faudrait reformuler. On va demander à Jens Maurer d'en tenir compte dans un truc que Jens Maurer fait déjà.
Item : le thread id n'est pas clair dans certains cas :
auto id1 = std::this_thread::get_id();
thread_local auto id2 = std::this_thread::get_id();
int main()
{
auto id3 = std::this_thread::get_id();
auto id4 = std::this_thread::get_id();
std::cout << id1 << std::endl << id2 << std::endl << id3
<< std::endl << id4 << std::endl;
}
On l'envoie à SG1, qui s'occupe de concurrence.
Item : clarifier la terminologie de « peut lever n'importe quoi » dans le cas de fonctions.
Item : on a une contradiction dans le sens qu'on donne à static_cast, const_cast et les transtypages de C. On en fait une priorité 2.
Item : avec l'écriture actuelle, ceci est illégal :
template <int* x = {}> struct X {};
Ça semble incohérent. Jason Merrill suggère qu'on considère {} comme une converted constant expression dans ce cas. NAD, devrait fonctionner. Par contre, Jason Merrill est disposé à retoucher la définition de converted constant expression pour que le texte soit plus près de l'intention.
Item : cas de déplacement d'un paragraphe d'une section à l'autre dans le standard, par souci de cohérence. NAD.
Item : il y a divergence sur la validité de ceci, à savoir s'il s'agit d'un contexte SFINAE ou non :
struct A { operator int(); };
template<typename T> T operator<<(T, int);
void f(A a) { 1 << a; } // operator<<(int,int)? Substitution Failure?
On va y aller avec la résolution proposée.
Item : il manque une retouche dans la définition des λ génériques pour exclure le cas où on a un truc comme :
auto f = [] () ->T // c'est le type de retour explicite qui la rend non-générique
{
}
Item : on nous rapporte que ceci devrait être bien formé, mais n'est pas permis par le standard :
template <class T, class U> struct X
{
typedef char member;
};
template<class T> struct X<T, typename enable_if<(sizeof(T)>sizeof(float)), float>::type>
{
typedef long long member;
};
int main()
{
cout << sizeof(X<double, float>::member);
}
On y va pour une priorité 2.
Item : avec le texte du standard, il n'est pas clair si ceci est légal ou non :
template<typename... T> void f(typename T::type...) { }
int main()
{
f<>();
}
Idem pour ceci, qui est plus complexe :
template<typename ...T> struct tuple {};
template<typename ...T, int, typename ...V>
tuple<T...> f(V...);
int main()
{
f<>();
}
La bonne nouvelle, c'est que tout le monde en déduit une séquence vide à l'appel, mais c'est difficile de voir ça dans le texte du standard. Un autre cas semblable est :
template <int> struct A;
template <int ...N, typename T> void foo(A<N> *..., T);
void bar()
{
foo<0>(0, 0); // Ok: N est un paramètre du template, T est int.
foo<0, int>(0, 0); // error: int does not match the form of the corresponding parameter N.
}
Richard Smith fait remarquer qu'on permet les parameter packs ailleurs qu'à la fin dans les fonctions, parce qu'on peut parfois s'en tirer avec les paramètres, mais pas pour les types. On a un priorité 1 ici, et faudra y revenir demain.
Item : on a un comportement indéfini avec ce qui suit, mais c'est discutable :
class B
{
public:
int f();
};
class C
{
public:
C(int);
};
class D
: public B, C
{
public:
D() : C(f()) // undefined: calls member function
// but base C not yet initialized
{
}
};
Je comprends l'intérêt, mais écrire la règle... Hubert Tong rappelle qu'il ne faut pas permettre ça avec une méthode virtuelle. Ici, la subtilité normative est que lors de l'appel de f(), on n'est pas (encore!) en train de construire D; la situation est qu'on a fini de construire B et on s'apprête à construire C. Il se peut que je m'en mêle.
Brève pause vers 15 h 30. On bavarde. Il y a de l'action chez EWG aussi, des trucs profonds. Un gros après-midi de réflexion pour tout le monde ici.
On a la visite de Robert Douglas pour N4417 v3. Faut examiner la formulation de son texte.
On vérifie les cas d'initialisation, la présente de feature test macros, ce genre de truc.
On a le terme presumed à divers endroits, ce qui a trait aux usages du préprocesseur pour __FILE__ et autres, mais Jens Maurer fait remarquer que ça ne s'applique pas à __func__ qui revient au compilateur.
Le type des fonctions qui retournent le numéro de lignes est discuté : le document indique size_t mais vu que __LINE__ est une macro, c'est pas clair que ce soit à propos. Hubert Tong vérifie le standard C, qui indique un requis minimal de 16 bits pour size_t alors que __LINE__ pourrait être 32 bits, alors faut au moins 32 bits.
Il y a aussi des retouches à faire dans la présentation (commentaires qui changent de ligne). Faut aussi traiter le cas de l'initialisation par agregate initializer (qui peut escamoter le constructeur).
Richard Smith indique qu'il souhaiterait avoir une latitude quant aux paramètres à la construction parce qu'un diagnostic est requis et que ça lui facilitera la vie. Il y a des should (ce qui signifie « implementation defined, but you are encouraged to get it right », dixit Richard Smith).
On discute de la nuance entre implementation-defined et unspecified pour les noms de fonctions rapportés par l'outil proposé : sont-ils décorés ou non, par exemple?. Pour le nom de la fonction, que fait-on dans une λ? Jonathan Caves pense qu'on a statué pour operator() dans ce cas. Roger Orr fait remarquer qu'un nom de fonction vide serait acceptable puisqu'on en a fait un cas de qualité d'implémentation. Hubert Tong fait remarquer que l'encodage de caractères est non-spécifié (on blague : Richard Smith suggère une saisie d'écran encodée comme une séquence de bytes, mais sans valeurs nulles; Jason Merrill suggère un uuencode.
Hubert Tong parle de la difficulté de déboguer pour trouver le paramètre qui nous fait souffrir dans un Parameter Pack. John Spicer fait remarquer que le concept de numéro de colonne est difficile à définir. Un participant souligne que si on considère unicode, ça peut devenir vilain. Hubert Tong fait remarquer qu'il est possible que la ligne rapportée par __LINE__ au moment où le préprocesseur intervient diffère de ce que le compilateur voudrait rapporter, alors on a encore ici un cas d'implementation-defined.
Robert Douglas lance en boutade qu'il se sentira personnellement insulté si les gens utilisent le fruit de son travail dans des macros, considérant ce qu'il fait justement pour se débarrasser de macros.
Item : les template templates avec paramètres par défaut. On n'est pas certains comment traiter ceci, qui semble raisonnable :
template<typename T1, typename T2 = char>
class A { };
template<template<typename... T> class X>
class S
{
X<int> x;
};
S<A> a;
Item : petite retouche à faire dans la formulation de l'édition des liens et des espaces nommés.
Item : on revient sur ceci, qu'on a déjà vu :
auto f() {
static int n = 123;
struct X { int &f() { return n; } };
return X();
}
int &r = decltype(f())().f();
L'idée est qu'il est maintenant possible de définir des fonctions dont le type de retour n'a pas de spécifications de liens. Il y a aussi des ennuis avec les fonctions extern "C".
Item : le standard n'indique pas si une spécialisation explicite d'une fonction template peut avoir un type de retour déduit. Est-ce grave? Jason Merrill dit que non, alors NAD.
Item : avec les espaces nommés inline, ceci semble brisé :
namespace A {
inline namespace b {
namespace C { struct X { }; }
}
}
namespace A { C::X x; }
C'est la définition de declarative region qui semble incorrecte ici. Jason Merrill est d'avis qu'un using namespace b; est implicite dans A. On note que l'exemple original lors de la soumission du bogue était plus complexe et plus réaliste :
namespace A {
inline namespace b {
namespace C {
struct X { };
}
}
}
namespace A {
namespace C { // <-- ah! C'est pas fin, ça
struct Y : X { };
}
}
John Spicer pense que ça ne devrait pas marcher : les espaces nommés inline devraient être transparents aux usagers, pas au concepteur. Cela dit, c'est pas unanime. Jens Maurer va essayer d'écrire quelque chose qui rendrait l'exemple légal.
Item : Hubert Tong fait remarquer que rien ne semble rendre ce qui suit illégal dans le texte du standard :
template <typename> struct A;
template <unsigned> struct A;
Ça rejoint le bogue de « listes de paramètres identiques » rencontré plus tôt. On va devoir définir le tout mais ça ressemble à la terminologie pour les signatures de fonctions
Item : le langage C supporte ça, mais voulons-nous vraiment le faire aussi?
struct A {
struct B { int x; } b;
int B; // Permitted in C
};
Hubert Tong suggère que oui, pour éviter d'introduire un cas d'exception. Jonathan Caves demande (en riant) s'il peut dire over my dead body. On signale que si int B est static, on a un A::B ambigu. Semble que les compilateurs l'acceptent... On est triste. Jonathan Caves dit que ça ne devrait pas fonctionner si B est un template.
Item : on a deux phrases à propos de decltype(expr) qui ne sont pas d'accord l'une avec l'autre. On a un jeu sur équivalence ou équivalence fonctionnelle :
template<typename, typename = decltype(sizeof(0))> struct X;
template<typename T> struct X<T, decltype(sizeof(T()))> {
typedef int type;
void f() {
X<T, decltype(sizeof(T()))>::type m; // ok, current instantiation
X<T, decltype(sizeof(int))>::type n; // error, not the current instantiation, need decltype
}
};
Jens Maurer va essayer de trouver une formulation.
Item : la définition de current instantiation of a partial specialization est incorrecte. Retouches terminologiques à faire.
Item : est-ce que type-dependent implique value-dependent?
template<int> struct X
{
typedef int type;
};
template<typename T>
struct Y
{
void f()
{ X<false ? this - this : 0>::type x; } // missing typename?
};
void g()
{ Y<void>().f(); }
Est-ce ce qu'on veut? On n'est pas sûrs... Jason Merrill et d'avis que le statu quo est souhaitable ici. Jens Maurer va l'écrire pour expliciter le propos.
Item : on a un passage sur les variadiques qui dit qu'exiger un Parameter Pack vide est incorrect, mais il semble que le permettre aux compilateurs pourrait être Ok. À suivre, faut y réfléchir.
On quitte les lieux. Ce soir, l'objectif visé est une séance de travail pour LWG, mais dans le lobby de l'hôtel pour éviter d'être coincés par l'heure de retour de l'autobus. Après avoir fait des trucs pour mes cours, je suis descendu dans le lobby de l'hôtel pour participer à cette séance.
La liste à examiner est disponible sur http://cplusplus.github.io/LWG/unresolved-prioritized.html
Item : clarifier les exceptions sur les flux. On va y penser...
Item : extraire des données d'un flux... avec Perfect Forwarding... et des char*... Ouch! Ce qu'on regarde, c'est un peu le gets() de C++! Review status
Item : semble être un truc terminologique. On n'y touche pas.
Thomas Köppe se joint à nous et offre le scotch. Merci!
Item : on impose aux facettes qui ont un service _byname de respecter un contrat sémantique sinon on a un comportement indéfini. Ok. Review status est envisagé, mais Jonathan Wakely a un malaise parce qu'il utilise des classes dérivées d'un parent facette en pratique (méchant Jonathan!). Open alors.
Item : l'écriture ne rejoint pas les attentes usuelles pour les postconditions et la gestion des exceptions (Marshall Clow souhaite un article sur le LWG issue 2136, qui est le problème général)
Item : il manquerait des constructeurs à regex<...>::match_results, que je ne connais pas. La bonne nouvelle : si on le fait, on ne brisera rien puisque personne ne les appelle présentement, par définition. Jonathan Wakely fait remarquer que les match_results ne peuvent être construits que par un regex, mais que les versions munies d'allocateurs sont pertinentes. Les opérations impliquant des allocateurs peuvent avoir des noexcept conditionnels puisque les allocateurs peuvent être Stateful maintenant. Il y aura des retouches terminologiques à faire.
Item : allocateurs Ok.
Item : définir program-defined, et remplacer user-defined par
program-defined. La subtilité : il y a beaucoup de trucs user-defined, et selon le standard la bibliothèque standard est
user-defined... Hubert Tong met les freins.
Richard Smith demande comment on qualifierait un truc comme std::swap<X>(X&,X&)
pour un type X maison devrait être qualifié. Thomas Köppe demande comment qualifier vector<int>. Ouf, je pense que c'est pas un truc qu'on va clore ce soir
.
Jonathan Wakely clarifie sa pensée pour les types comme vector<int>.
J'aimerais clore l'item 2372, mais c'est mal reçu... On parle de l'initialisation d'une std::string avec un entier. C'est le cas 0, pointeur nul, qui est un problème (les autres, je m'en fous un peu).
Enfin, petite jasette avec mon amoureuse et dodo. Il reste une grosse, grosse journée demain, et plusieurs votes samedi matin avant de reprendre l'avion vers la maison...
Sympathique surprise ce matin : j'ai croisé Marshall Clow dans l'ascenseur, en allant vers l'autobus. Marshall Clow est celui qui dirige les travaux du LWG, alors c'était avec lui que nous travaillions hier et avant-hier soir. Il se trouve qu'avec lui, nous étions quatre dans l'ascenseur, alors il nous a offert un lift vers les lieux des travaux.
De quoi parlent quatre informaticiens dans une voiture pour une route de
vingt minutes? De météo, évidemment
Il se trouve
que l'un d'entre nous venait de Calgary mais habite maintenant New York,
Marshall Clow lui-même vient de San Diego, et moi de la région de Montréal (je n'ai
pas bien compris d'où venait notre autre compagnon, plus timide), alors les
expériences sont plutôt différentes.
Petit échange avec Gor Nishanov au déjeuner. Il a travaillé jusqu'à tard hier soir sur la terminologie de sa proposition sur les coroutines, alors on la verra peut-être passer aujourd'hui, qui sait? J'aimerais bien.
On commence par le document retouché par Robert Douglas sur le Source-Code Information Capture, où il a tenu compte des recommandations faites par CWG hier.
Il reste des trucs mineurs à retoucher, comme le choix de police de caractères à certains endroits, mais c'est à peu près ça. La terminologie de la méthode current() est particulière parce qu'elle laisse de la latitude aux implémentations quant à ce que signifie « la ligne courante » dans le code source.
Les choix des mots sont très importants à CWG : le terme implementation-defined fields, par exemple, fait réagir car le mot fields n'est pas normé (on préfère implementation-defined value à implementation-defined fields).
Hubert Tong se demande quel sens on devrait donner au constructeur par défaut, ce détail n'étant pas clair à ses yeux. Il n'a pas tort : le document ne le dit pas!
Nous avions souligné hier le problème du choix d'un type pour représenter le numéro de la ligne courante dans le code source, le choix initial dse Robert Douglas ayant été size_t, ce qui semble raisonnable à ceci près que ce type entier non-signé pourrait, dans des cas dénégérés, être encodé sur 16 bits. Robert Douglas a mis unsigned long long comme type pour les numéros de lignes parce que le standard impose un minimum de 32 bits pour ce type, mais on se demande pourquoi on n'a pas demander uint_least32_t. Jens Maurer fait remarquer que le type unsigned long long pourrait être 64 bits et donc, sur une machine 32 bits, pourrait utiliser deux registres. Richard Smith fait remarquer que les valeurs calculées doivent l'être efficacement, mais les types utilisés à l'interne n'ont pas à être les mêmes que ceux exposés par l'accesseur.
Dinka Ranns demande si le document retournera à LEWG ensuite; oui, on ne fait que retravailler la formulation.
On revient sur D4518, qui propose d'intégrer les spécifications d'exceptions au système de types.
Il reste une coquille dans un des exemples sur les pointeurs de fonctions. Hubert Tong demande si les pointeurs avec et sans noexcept sont compatibles pour ce qui est des va_arg calls. Richard Smith pense que oui. Jens Maurer ajoute une note pour en tenir compte (ici, faut distinguer va_arg, qui touche surtout à la compatibilité avec C, langage qui ne supporte pas les exceptions, des templates variadiques, qui touchent à C++ et sont conscients des exceptions)). Jason Merrill est inconfortable avec le changement et préférerait le statu quo; on revient sur nos pas.
Dinka Ranns s'interroge sur le nombre de conversions à faire lors de conversions de fonction f(T) noexcept à fonction f(T). On clarifie et on allège un peu, à quelques endroits. Une partie du travail est de « normer » le texte de manière à ce que les compilateurs soient en mesure de bien résoudre les ambiguïtés entre signatures distinctes de fonctions, même si nous ajoutons les spécifications d'exceptions dans la liste de critères et même si on ne peut surcharger les fonctions sur cette base.
RS demande si affecter une λ sans capture qui ne lève rien, comme la λ minimale []{}, à un pointeur de fonction noexcept comme void(*p)() noexcept, demeure légal. Avec les changements, ce code devient illégal alors qu'il passait avec C++ 14 :
void g1() noexcept;void g2();
template<class T> int f(T *, T *);
int x = f(g1, g2);
Quand on examine les surcharges possibles, avec ou sans noexcept, on a le cas des using à examiner, qui peuvent introduire des noms dans le lookup contextuel et provoquer des ambiguïtés, ce qui est correct ici mais doit être permis par l'écriture du standard.
Note personnelle : on peut lever des
pointeurs de fonctions en tant qu'exceptions. Il y a quelque chose à faire avec ça
. Cependant,
ceci est illégal :
#include <iostream>
using namespace std;
void g()
{ throw []() { cout << "Wow!" << endl; }; }
int main()
{
try
{ g(); }
catch (auto &f)
{ f(); }
}
Ceci compile, mais plante à l'exécution :
#include <iostream>
#include <functional>
using namespace std;
void g()
{ throw []() { cout << "Wow!" << endl; }; }
int main()
{
try
{ g(); }
catch (function<void()> f)
{ f(); }
}
Ceci par contre fonctionne partout :
#include <iostream>
using namespace std;
void g()
{
throw (void(*)())([]() { cout << "Wow!" << endl; });
}
int main()
{
try
{ g(); }
catch (void (*f)())
{ f(); }
}
J'ai vérifié auprès des collègues ici et le crash tient du fait que les clauses catch sont limitées dans ce qu'elles réalisent comme conversion et n'appellent pas les opérateurs implicites de conversion du tout. Avec un catch(...) dans main(), on ramasserait la λ sans planter, mais on ne pourrait pas vraiment s'en servir.
Pause brève.
Richard Smith suggère une petite retouche au document intégrant les spécifications d'exceptions au système de types. On le soumettra au vote cet après-midi. On ne pensait pas mettre de macro pour fins de Feature-Test parce qu'on ne voit pas de moyen raisonnable d'écrire du code qui compile conditionnellement sur la base de cette facilité ou de son absence, mais on en ajoute une par acquis de conscience.
Pour le document sur la mémoire transactionnelle, on doit valider la conformité des changements que
Michael Wong a faits. On fait le tour et ça semble beau.
Richard Smith dit qu'il a
des doutes sur les teintes de vert pour les sections contenant des changements,
mais on va faire avec
. Ok. On
change le « D » pour un « N » sur le document pour qu'il passe de « en
développement » à « normatif ».
On a encore 57 minutes alors on va examiner la proposition de Gor Nishanov (qui vient nous rejoindre) sur les coroutines. Celle-ci ne passera pas cette semaine, mais on a des chances pour Kona.
Hubert Tong demande si on doit prévenir des coroutines qui seraient extern "C". Gor Nishanov signale que les coroutines sont en fait des fonctions donc que c'est pas grave.
Jason Merrill demande où retourne un cowait appelé dans un constructeur ou dans un destructeur; évidemment, les fonctions spéciales ne peuvent être des coroutines mais elles peuvent les appeler. Gor Nishanov explique le modèle mental à adopter avec sa proposition pour raisonner sur le code.
Quel est l'impact d'utiliser une coroutine pour initialiser une variables statique? Faut utiliser coawait, comme on le ferait ailleurs.
Hubert Tong demande ce qui se passe si on longjmp dans une coroutine suspendue; Gor Nishanov dit que c'est pas fin. Il faut mettre des mots à ce sujet.
Dans les exemples, on a des suspend resume point 0 et on se demande quel sens donner au « 0 »; Gor Nishanov dit qu'il cherche une notation pour indiquer que c'est là que se trouve le code spécial à générer. Le rôle de cette chose est de signaler un point de retour, une sorte d'étiquette. En pratique, ce serait une surcharge d'operator,() mais ça se lisait drôle. Richard Smith suggère une pseudo expression.
Les mots comme cofor sont un peu bof... On cherche une alternative. Gor Nishanov dit qu'il aimerait bien cor. Je suggère gor, à titre éponyme. On rigole un peu.
On demande ce qu'on obtiendrait avec :
future<int> f();
// ...
decltype(coawait f);
Gor Nishanov dit que ça échouerait probablement. Bien sûr, decltype(coawait f()) serait int.
Quelqu'un demande si on pourrait ajouter un coto, à l'image du goto. On rigole.
Hubert Tong demande pourquoi on ne peut passer que des types déplaçables à des coroutines. Gor Nishanov explique qu'en général, on n'a pas de pile alors il nous faut ce mécanisme. Hubert Tong demande pourquoi on n'accepte pas la copie. Gor Nishanov pensait l'avoir permis. On retouche.
John Spicer demande un exemple dans la section 8.4.4. Hubert Tong ajoute que pour des points bonis, faudrait que ce soit une λ.
C'est extra intéressant mais il y a beaucoup à lire et à faire. On suspend (!) vers midi. Cet après-midi, on a une plénière à l'horaire. Hubert Tong me recommande de me trouver une place tôt, ce que je fais. Il y a de la politique dans les alentours.
Je vais me chercher une salade que je mange en écrivant ceci, mais une de mes olives contient encore un bout de noyau. Ouch. Ça surprend sur une molaire, ce truc.
Plusieurs personnes ne sont pas allées manger, et pas les moindres. Il y a de la nervosité dans l'air. Clark Nelson, le chair, est à son poste durant toute la pause de dîner, alors que la plupart des gens les plus connus bavardent dans la salle de plénière en attendant que le tout reprenne.
Notez que je vais cesser en grande partie le nominatif dans ce qui suit, par respect pour les participant(e)s car les débats ont été chauds.
Les règles pour la plénière semblent être que les chair des SG et des WG doivent s'assurer que les propositions à débattre soient écrites, nettoyées et prêtes à être présentées pour 13 h 30. Des Progress Reports seront présentés sur chacun des dossiers chauds/ chaque famille de dossiers chauds (il y en a 15, à l'oeil). Certains des SG sont fermés, ayant terminé leur travail, et le fruit de leur boulot étant traité dans des WG.
Le SG2 sur les modules est inactif.
Le SG3 sur les systèmes de fichiers est inactif.
Le SG5 sur la mémoire transactionnelle : Michael Wong dit qu'on a atteint notre objectif. Le TS est prêt à être voté. CWG proposera d'adopter le TS. On continuera par la suite à examiner ce qui pourrait être développé de ce côté pour Kona, et on l'implémentera au moins dans gcc et dans clang.
Le SG6 sur les numériques : Lawrence Crowl dit qu'on a un début de TS qui se tient pas mal. Ça avance bien. On ne sera pas prêt pour un TS à Kona, mais probablement au WG21 suivant.
Le SG7 sur la réflexivité : Chandler Carruth dit qu'on a eu plusieurs propositions, et on a de grosses décisions de design à prendre. Quelques trucs se promènent dans LEWG. On aura d'autres propositions pour Kona. Je ne sais pas quand on va livrer un TS car les décisions sont lourdes. On n'a pas de nouvelles pour les Type Property Queries car l'auteur n'était pas présent.
Le SG8 sur les concepts est inactif
Le SG9 sur les intervalles : Marshall Clow dit qu'ils ne se sont pas rencontrés. Il y avait une proposition, mais c'était le gros document d'Eric Niebler, qui est allé à LEWG en tant que proposition. Les explorations électroniques se poursuivent, mais on va au moins continuer de faire progresser les travaux autour de la proposition d'Eric Niebler. Ça va se passer en bonne partie à LEWG.
Le SG10 sur le Feature Testing : Clark Nelson dit que le groupe s'est rencontré et a réussi à intégrer des Feature Test Macros dans quelques TS. Ce que a été fait pour C++ 14 était tardif et un peu faible, mais ils pensent faire un meilleur boulot pour C++ 17. On a un texte Boilerplate pour les TS d désormais. Gabriel Dos Reis ajoute qu'à son avis, le texte de Feature Test devrait être obligatoire dans chaque TS. Clark Nelson dit qu'une explication des raisons pour lesquelles ce pourrait ne pas être nécessaire sera aussi acceptable. John Miller parle de nuances entre les requis pour les TS et ceux pour les IS.
Le SG12 sur le comportement indéfini : Gabriel Dos Reis indique que le groupe ne s'est pas rencontré car ses participants-clés étaient dans CWG et LEWG.
Le SG13 sur les entrées/ sorties : Herb Sutter dit que le groupe de s'est pas rencontré. L'affichage 2D n'est complété qu'à 80%, mais c'est très gros (heureusement, l'auteur est un ex-avocat). On compte l'examiner d'ici Kona et le présenter alors.
Le SG1 sur la concurrence : Hans Boehm dit que deux TS ont été traités par eux cette semaine :
Dans le collimateur :
Autres trucs divers discutés cette semaine : is_always_lock_free(), les coroutines (avec EWG), les promesses, la mémoire Thead-Local, memory_order_consume, les atomiques, le π-calcul, etc.
EWG : Ville Voutilainen dit qu'ils ont traités presque toutes les propositions apportées. Ont donné des orientations pour les tableaux dynamiques, la mémoire transactionnelle et les concepts. Les comparaisons par défaut et les fonctions résumables ont aussi avancé. Ils n'ont traité aucun « issue », faute de temps. Les modules avancent. Les contrats aussi. Pas de consensus pour la syntaxe uniforme des appels de fonctions. Les stackful coroutines ne sont pas un dossier clos. Ils ont une courte liste de propositions acceptées, neuf cas de ropositions rejetées.
CWG : Mike Miller parle de notre traitement des commentaires sur les concepts et la mémoire transactionnelle, du travail sur la terminologie et les formulations relayées par EWG et LEWG, et d'un grand nombre d'« issues ».
Pour les concepts :
Pour la mémoire transactionnelle :
Autres propositions examinées et par EWG et examinées CWG :
Autres propositions examinées et par LEWG et examinées CWG :
Il nous reste aussi des « issues » à catégoriser. On passe au vote pour les trucs mis de l'avant pour CWG :
LEWG : Jeffrey Yasskin dit qu'ils ont couvert une trentaine de propositions. Ils ont pris le temps de les mettre en ordre, et les quatre qu'ils n'ont pas traité sont telles que les auteurs n'étaient pas ici. Il estime que son groupe a été efficace. Ils ont beaucoup parlé de variants, d'intervalles et de réseautique. Il invite les gens à le document d'Axel sur les variants pour se faire une tête. Pour les intervalles, il leur reste un détail à regarder et comptent le faire dans une téléconférence. Pour la réseautique, ils poursuivent les débats demain.
LWG : Marshall Clow dit qu'ils ont travaillé fort. Ils avaiet moins de propositions qu'à l'habitude. Ils ont vérifié deux TS, soit un sur la concurrence et un autre sur les points de personnalisation, ce dernier par Eric Niebler. Pour les points de personnalisation, ça avance bien. Il reste un peu d'expérimentation à faire.
Ils ont examiné une proposition sur des traits nothrow-swappable. Ce document proposait trois approches, et LWG a donné sa préférence. Ils ont aussi examiné une proposition sur le type des accumulateurs, mais n'y ont pas été favorables sur la base de prémisses inexactes.
Marshall Clow liste ensuite plusieurs propositions qui ont été discutées.
Quand on vient pour voter, quelqu'un s'aperçoit que les liens menant sur les propositions à voter mènent sur les textes pré-rencontre, et ne tiennent pas compte des changements apportés cette semaine. C'est une erreur technique, mais ça bloque le vote. Alisdair Meredith s'offre pour générer un document « N » pour ce soir si on veut le lire, ce qui permettrait de voter demain. Stephan T. Lavavej dit que dans bien des cas, les implémenteurs feront les changements qu'il y ait vote ou non, car les changements sont triviaux et utiles, mais se sentiront mal si on ne les appuie pas. Jens Maurer a des réserves sur des gestes précipités. John Spicer aussi.
Clark Nelson remarque qu'un groupe de liens mène effectivement sur les documents les plus récents. On pourrait stabiliser la première moitié, plus pressante et plus stable, et remettre les moins pressants (la deuxième moitié) à demain. Jeffrey Yasskin appuie la démarche d'Alisdair Meredith. Marshall Clow dit que le problème est plus complexe dans d'autres cas.
Les échanges sont techniques, mais l'idée ici est qu'on ne veut pas voter sur des trucs qui (a) ne sont pas sur des liens immuables et (b) n'ont pas circulé pour lecture avant la rencontre.
On s'entend sur une procédure. On votera demain, dans le cas des dossiers qui auront été remis à jour (probablement d'ici 17 h aujourd'hui, selon Howard Hinnant). Marshall Clow, qui est franchement très gentil et vient de vivre une heure très longue à gérer ça en public, remercie des gens.
On y va avec les votes :
Ici, à 15 h 25, Clark Nelson suggère qu'on prenne une pause. Pourtant, il ne reste qu'une seule étape... Mais elle est politiquement chargée
.
On discute enfin du conditionally-supported special math functions v3, version amendée (N4337). Ouf. http://www.open-std.org/jtc1/sc22/wg21/docs/papers/2015/n4437.pdf
Marshall Clow fait un retour historique sur cette question. Le IS pour les fonctions mathématiques spéciales remonte à loin. Walter Brown a écrit un texte sur le support conditionnel de ces fonctions en C++. Il y a eu beaucoup de débats sur le sujet cette semaine, plusieurs votes. On a décidé de venir devant la communauté entière, initialement avec l'idée en enlevant le volet obligatoire. Cependant, aujourd'hui, on a choisi de proposer un support obligatoire de ces fonctions dans le standard.
Question : doit-on voter aujourd'hui pour renouveler l'ancien standard non-appliqué?
Réponse : ça peut attendre à Kona
Commentaire : Dinkumware a implémenté ceci, ça a été fameusement difficile, et on a réussi. J'ai commis l'erreur de dire aux gens de WG21 à quel point ça avait été ardu et les gens n'ont plus envie de l'implémenter. Toutefois, il y a maintenant une option tierce, soit une implémentation à code ouvert de ce standard. Je pense que ça change la dynamique dans ce dossier
Commentaire : je maintiens une version d'un produit à code ouvert et c'est pas simple
Question : doit-on avoir une implémentation de cette bibliothèque dans une implémentation Free-Standing de C++?
Réponse : non
Commentaire : la communauté scientifique aime C++, et ces fonctions sont celles dont on a besoin pour faire notre travail. Supporter notre communauté rapportera à C++ tout entier
Commentaire : je ne suis pas opposé à un support conditionnel. Un support obligatoire me rend moins heureux. Le niveau technique des clients et des bogues à régler va nous vider de notre énergie. On préférerait la livrer si cela nous semble raisonnable d'un point de vue commercial. Ça coûte cher de supporter ce genre de truc
Commentaire : je ne suis pas opposé à ceci, mais je n'aime pas que ce soit dans <math.h> car on ne le contrôle pas. Si on le place dans <cmath>, que nous contrôlons, c'est Ok
Commentaire : j'ai parlé à bien des gens car j'ai vu venir ces débats toute la
semaine. Quel est l'effet d'un support conditionnel? On a des trucs comme ça dans
Réponse :il y en a une par Dinkumware
Commentaire : je suis conscient de l'importance de ceci pour la communauté scientifique. Je pense que ce qui est là peut aussi nous apporter des avantages dans plusieurs autres domaines.
Commentaire : c'est utile pour la civilisation toute entière, bien sûr, mais il existe des implémentations de ce truc et on sait où aller les chercher. Je pense aux coûts pour les petits vendeurs, dans le domaine des systèmes embarqués par exemple : si une entreprise de la taille de Microsoft trouve ça coûteux, que feront les plus petits vendeurs?
Commentaire : ce ne sont pas des bonbons, ce sont des fonctions mathématiques pertinentes et utiles, faciles à utiliser pour les spécialistes. Elles sont clairement spécifiées mais difficiles à implémenter. Je pense que ça a sa place dans le standard. Je ne suis par contre pas sûr qu'on est prêts à procéder. Si un individu avec quelques PhD bien se plaindre qu'on a un bit incorrect, on sera bien mal pris de l'aider. Je pense qu'on devrait y aller graduellement, et développer au passage l'expertise requise pour éventuellement le supporter
Commentaire : pour nous, un support conditionnel est une mauvaise chose. Je sais que c'est la situation actuelle. Ma communauté m'a envoyée ici parce qu'elle veut savoir si les millions de lignes de code que nous avons aujourd'hui fonctionnera encore dans dix ans. On n'utilise que du code standard pour cette raison, et on ne peut utiliser les fonctions mathématiques spécialisées pour cette raison. Avoir ces fonctions sera un gros avantage à nos yeux
Commentaire : je suis d'accord pour dire que le statu quo ressemble à la situation actuelle. Je reconnais le risque d'un C++ modulaire. Je crains qu'une entreprise réalisant un contrat gouvernemental se trouve coincé à offrir ces fonctions et doive imposer à ses fournisseurs d'outils de supporter ceci
Commentaire : je suis surpris qu'on ait collectivement discuté de support conditionnel d'outils de nature scientifique, des choses qui comptent, mais qu'on estime correct discuter de trucs simples comme optional
Commentaire : on parle de 21 fonctions. Boost en a une partie. Il est possible de réduire les coûts d'implémentation de cette bête. Ces fonctions sont très importantes pour mon entreprise, pour les statistiques, les calculs de type « haute performance », le domaine financier, etc. Et ce sont des trucs que vous ne voulez pas implémenter vous-mêmes. Je pense qu'on devrait considérer très sérieusement de les supporter de manière inconditionnelle. Dans bien des contrats, on exigera un langage de programmation avec un numéro de standard ISO. Le support conditionnel n'est pas à notre avantage, surtout pour 21 fonctions bien documentées et bien comprises par leurs clients
Question : est-ce que la communauté C a examiné cette question?
Réponse : on ne révise pas le standard C pour le moment, alors non
Réponse : ils ont fait ça avant nous!
Réponse : c'est un TS pour C 11
Commentaire : le nombre de fonctions n'a rien à voir avec la complexité d'implémentation. C'est des années de travail et de tests. Si on indique mandatory, on impose une pression sur le vendeur; si on le garde optionnel, ça devient une décision de marché. Les usagers peuvent choisir le fournisseur de leur choix. Le marché peut changer
Question : quelle est la licence pour l'implémentation à code ouvert? Aussi : devrais-je la vouloir implémentée sur mon téléphone?
Commentaire : le problème du support conditionnel est que ça nous empêche de les utiliser. Elles ne sont pas standards au sens où nous l'entendons
Commentaire : je pense qu'on devrait voter
Commentaire : le libellé du vote tel qu'écrit me semble correctement écrit.
Question : j'aimerais savoir la réponse à la question pour la licence
Réponse : pour la version du TS, c'est une licence GPL v. 3 avec une retouche pour faciliter la diffusion non-virale. La version Boost est sous licence Boost
Commentaire : j'ai entendu plusieurs nouveaux arguments, et l'argument pour une inclusion non-conditionnelle me porte à vouloir réfléchir. J'aimerais digérer cette nouvelle information et me faire une tête. Est-on pressés? Je pensais que nous offrions un excellent support à la communauté scientifique et je suis un peu secoué
Commentaire : j'aimerais qu'on vote et qu'on se permettre de revisiter la question ultérieurement si nous l'estimons pertinent
Commentaire : je n'achète pas l'argument selon lequel un support conditionnel est un obstacle à l'adoption. On a déjà une situation de support variable pour plusieurs caractéristiques du langage selon les implémentations
Commentaire : on n'a pas à juger des décisions d'entreprise de nos clients
Commentaire : si on l'adopte, il est probable que plusieurs implémenteurs se mettront à l'implémenter rapidement. Sur la base de ce que j'ai entendu aujourd'hui, je vais supporter fortement la proposition
Commentaire : j'ai essayé de ne pas parler. Peu importe la décision prise aujourd'hui, je pense que nous sommes parvenus à informer les gens sur place d'une préoccupation bien réelle de la communauté scientifique et numérique. Ce que nous n'avons pas clairement exprimé, c'est pourquoi ce sont ces fonctions qu'il nous faut (il trace un historique de cette question). Adopter ces fonctions dans le standard est un message que nous envoyons à la communauté qu'elle n'est pas un « enfant bâtard ». (note personnelle : oups! Ça dérape). Je sais que je suis hyperbolique parfois. Je ne reviendrai pas sur le sujet, je passe la torche à la communauté, qui vous envoie (à ma surprise) un message bien plus clair que je ne l'aurais cru
Commentaire : je m'objecte fortement à ce que ce vote puisse être considéré comme un camouflet à une communauté
Commentaire : désolé d'avoir ri, mais j'ai trouvé le choix du mot « hyperbolique » délicieux dans les circonstances (rires!)
Pour la motion de support inconditionnel : on a un vote pour, mais avec autant d'abstentions que de non, et plus de « non-oui » que de oui. Quelqu'un fait remarquer que les contre lui semblent principalement être les implémenteurs, alors que les pour sont principalement les usagers.
Commentaire : je ne pense pas qu'on puisse parler d'un fort consensus
Commentaire : je suis légèrement contre cette proposition, mais j'espère voter à nouveau dans le futur et être pour. Je suis fortement contre le support conditionnel, par contre. Je pense que c'est la pire des situations
Commentaire : je ne suis pas convaincu que le vote a vraiment opposé les usagers et les implémenteurs
Commentaire : le deuxième vote qui s'annonce, à propos d'un support conditionnel, me semble différent du statu quo (il lance une boutade qui fait rire les gens, mais je l'ai manquée)
Commentaire : si on ajoute un support conditionnel de ceci dans un TS, il y aura des réactions et je veux diffuser la nouvelle plus largement, question d'avoir le point de vue des gens hors de la salle et éviter une explosion dans 18 mois
Marshall Clow (je le nomme ici car il parle à titre officiel et technique pour LWG) : je veux rappeler aux gens dans la salle que les vendeurs n'ont pas le droit d'ajouter des noms dans std à leur convenance. Ainsi, si on n'a pas de support conditionnel, ces fonctions devront être hors de std
À titre d'information, on ne peut ajouter de « beaux » noms à l'espace nommé standard; si on met des noms là, faut qu'ils soient enlaidis (Uglyfied) pour ne pas entrer en conflit avec les noms standards.
Aussi, si ça vous intéresse, dans les documents de la bibliothèque standard, ils n'utilisent pas le préfixe std::
de manière systématique, pour laisser les gens utiliser leurs propres fonctions en lieu et place des version standards s'ils le désirent – c'est ce qu'on nomme des points de personnalisation – sauf dans deux cas : std::move()
et std::forward(). Ceux-là, c'est « pas touche »
.
Commentaire : je crains qu'on ne crée un précédent en ayant une bibliothèque avec support conditionnel
Commentaire : je ne comprends pas exactement ce que l'on mettrait dans le sondage envoyé à tous
Réponse : le document entier, pour éviter les écueils dans le futur
Commentaire : je crains que les implémenteurs ne votent pour ceci qu'en pensant que c'est moins pur que le vote précédent. Si vous ne voulez pas l'implémenter, votez contre. Ça ne sert à rien d'envoyer un faux message à la communauté
Commentaire : on a quelques exemples de support conditionnel. Le over-alignment, par exemple. La collecte d'ordures aussi. C'est là mais ça peut être des noop
Commentaire : j'aimerais vraiment qu'on remette tout ça à Kona. Je suis encore sous le choc de la force des échanges cet après-midi
Commentaire : je pense qu'il faudrait voter aujourd'hui. Je comprends que [...] est au bout de ce qu'il était disposé à faire dans ce dossier. Nous avons au moins quatre Emeritus aujourd'hui, ce qui est exceptionnel, et je ne suis pas sûr qu'on ait cette représentation à Kona
Commentaire : un support conditionnel crée les noms et met le support du texte entre les mains du WG21. Un non-support n'a pas ces qualités
Question : quelle est l'interprétation de notre vote?
Réponse : c'est un consensus, mais pas un consensus fort.
Commentaire : c'est du deux pour un si on compare les oui et les non
Commentaire : je comprends, de l'interprétation qui est faite ici, qu'on donne beaucoup de poids aux implémenteurs dans le vote
Commentaire : je serais nerveux de voir ce vote comme un consensus. C'est la décision de [...] mais je suis à l'aise avec son interprétation
Commentaire : sur la base de l'interprétation de [...], on pourrait implémenter toutes ces fonctions et retourner NaN. Ce serait une implémentation, mais elle ne serait pas très bonne (on rigole).
Commentaire : j'aimerais qu'on remette ça à Kona. Je ne nous vois pas faire beaucoup de progrès.
Pour la motion de support conditionnel : on a seulement quatre pour, alors on ne regarde pas le reste.
Commentaire : je propose que l'on reprenne le premier vote étant donné les échanges depuis
Il y a de l'ouverture. Deux personnes disent être opposées car le vote a eu lieu. Marshall Clow suggère qu'on attende à demain. D'autres insistent pour le reprendre maintenant. Ça brasse un peu.
Pour la motion de support inconditionnel, deuxième essai : on a un vote pour, mais avec à peine moins de non, toujours autant d'abstentions que de non, et plus de non-oui que de oui
Ouf. Marshall Clow va se rasseoir et on l'applaudit.
Clark Nelson : on examine les prochaines rencontres
On ajourne vers 17h15
On est tous pas mal brûlés. On se disperse, en voiture ou en autobus (ce qui est mon cas). Parle parle, jase jase. Il y aura des sessions de travail individuelles ce soir mais rien de formel et collectif.
Demain, je ne sais pas si les travaux seront terminés quand je quitterai, car mon avion quitte Kansas City vers 15 h 30 et il y aura probablement des votes en début d'après-midi. On fera avec; ça fera partie de mon apprentissage et je le saurai pour la prochaine fois.
Ma femme me manque. Mes enfants aussi. Ce fut une belle et intense semaine, mais je serai bien content de dormir chez moi demain soir. Tard, mais auprès de ma belle.
Logistique du départ ce matin. Vérifier l'état de la carte de crédit : c'est un peu serré selon mes calculs, alors je viens pour faire un transfert de fonds (j'aurais pas pu le faire longtemps avant, la paie étant avant-hier) pour m'apercevoir qu'on est samedi alors le transfert que je viens de faire ne prendra pas effet tout de suite. Bon, j'avais besoin de ça. Mon erreur. Ça devrait fonctionner, mais j'aurais préféré être moins près de ma limite disons, question de confort.
L'hôtel a glissé la facture sous ma porte, comme c'est l'usage. J'ai eu du très bon service cette semaine. Je glisse un peu d'argent dans l'enveloppe destinée aux gens qui font l'entretien et je leur écrit un petit mot (je ne peux pas leur donner beaucoup, mais je me dis qu'ajouter un merci ne peut pas être désagréable). Je ramasse mes trucs, ce qui va quand même relativement rapidement car je ne prends pas beaucoup d'espace (un tiroir, quelques trucs à la salle de bains, mes quelques chemises). Quand je voyage seul, je m'en sors avec un sac pour les vêtements et mon équipement d'ordinateur portatif. Ça sauve des frais et du temps.
Il y a une erreur sur la facture, qui m'a chargé les frais d'accès à Internet pour l'une des six journées alors que je devais l'avoir gratuitement toute la semaine, mais ils ont corrigé l'erreur sans faire de chichis, ce qui est tout à leur honneur. J'ai aussi demandé au concierge du lobby d'appeler le service de navette pour moi, question d'éviter les imbroglios liés à mon accent. Comprenons-nous : mon anglais est excellent et passe très bien en personne, mais au téléphone, la rencontre de l'accent montréalais et de l'accent du midwest américain, ça se gère mais ça accroît les risques d'erreur, et je ne pourrai pas vraiment me permettre de délais quand je quitterai le quartier général de Perceptive Software tantôt.
J'écris donc ceci du lobby de l'hôtel, où je me suis installé pour profiter de l'accès Internet gratuit puisque je ne l'ai plus dans ma chambre (que je viens de quitter de toute manière). Ce matin, il nous reste des travaux en CWG, puis une série de votes devra se prendre mais à mon avis, je ne pourrai pas participer à cette partie du processus, question de délais de transport. Ce sera pour la prochaine fois; l'essentiel de la grosse politique s'est faite hier, à mon avis.
Petite discussion avec Walter Brown, sur les événements d'hier et sur la vie en général. Un très charmant monsieur que celui-là. En toute honnêteté, et je l'avais remarqué à CppCon 2014, l'immense majorité des gens ici sont très gentils. Walter Brown m'a proposé deux projets étudiants qui pourraient être fort intéressants à faire progresser, dont un qui pourrait « coller » à ce que je fais dans mon cours de parallélisme et systèmes concurrents cet été. À suivre.
J'ai bavardé un peu avec Gor Nishanov en attendant l'autobus, à propos de sa proposition de coroutines. Parle parle, jase jase, il a eu la gentillesse de m'offrir un lift alors j'ai fait le chemin avec lui et Stephan T. Lavavej ce matin. Gor Nishanov m'a parlé un peu de sa jeunesse en Russie et il se trouve qu'il parle quelques mots de français. Ce fut bien agréable. On a travaillé un peu ensemble sur un paragraphe subtil de sa proposition avant le début des travaux formels.
On compte faire de la priorisation de « issues » ce matin. Jens Maurer fait remarquer que pour les coroutines, il y a un risque que la reprise d'une coroutine après un point d'arrêt soit faite dans un thread distinct du thread original, ce qui peut compliquer (c'est le moins qu'on puisse dire!) le concept de happens-before puisqu'on perdrait la relation sequenced-before.
On reprend les discussions sur l'item 2067. Qu'est-ce qui, dans le texte normatif, permettrait de ne générer que des templates à zéro paramètres? Le code qui a mené à la détection du problème est :
#include <utility>
template <template <class...> class T, class...X>
struct Part
{
template<class...Y>
using type = T<X..., Y...>;
};
template <template <class...> class T, template <class> class U>
struct UCompose
{
// Not ok!
template <class X, class...Y>;
using type = T<U<X>, Y...>;
//// Ok!
//template<class X>
// using type = T<U<X>>;
};
int main()
{
using IsInt = Part<std::is_same, int>;
static_assert(IsInt::type<int>::value, ""); // Ok!
using IsIntD = UCompose<IsInt::type, std::decay_t>;
static_assert(IsIntD::type<int&>::value, ""); // Not ok.
}
Par contre, semble que ceci fonctionne :
template <template <class...> class T, class...X>
struct Part
{
template <class...Y>
struct result
{
using type = T<X..., Y...>;
};
};
template <template <class...> class T, template <class> class U>
struct UCompose
{
template <class X, class...Y>
using type = typename T<U<X>, Y...>::type;
};
int main()
{
using IsInt = Part<std::is_same, int>;
static_assert(IsInt::result<int>::type::value, ""); // Ok!
using IsIntD = UCompose<IsInt::result, std::decay_t>;
static_assert(IsIntD::type<int&>::value, "");
}
Le standard dit If every valid specialization of a variadic template requires an empty template parameter pack, the template is ill-formed, no diagnostic required, mais il y a clairement un intérêt à la manoeuvre.
Item 2068 : dans un cas comme ceci, le destructeur de B devrait pouvoir être défini implicitement, mais le texte du standard ne le spécifie pas :
struct A { virtual ~A(); };
struct B : A {};
int main()
{
A *p = new B;
delete p;
}
Jason Merrill fait remarquer qu'on touche ici aux optimisations de dévirtualisation, et que ça entraîne du plaisir d'accès aux symboles cachés dans des bibliothèques partagées. Jens Maurer demande si les destructeurs sont des citoyens de seconde classe pour fins d'optimisation par dévirtualisation. Jason Merrill dit que oui, essentiellement. Richard Smith estime qu'on ne devrait pas être obligés de le définir si on ne l'estime pas nécessaire. Richard Smith dit qu'on a des cas pour d'autres fonctions aussi. Ici, la fonction est odr-used parce qu'elle est virtuelle sans être pure.
Item 2069 : les destructeurs ont-ils des noms? Non, mais on a une phrase qui laisse entendre que oui. Faut la réécrire. Hubert Tong fait remarquer qu'on pourrait avoir plusieurs noms pour un même destructeur; Richard Smith signale que c'est le cas avec les opérateurs de conversion. Clairement, dans 3.4.3.1, on peut lire A destructor's name is looked up... qui est incorrect.
Item 2070 : on l'a couvert indirectement plus tôt cette semaine (question de lookup de noms d'espaces nommés), mais c'est plus subtil car on examine des questions d'alias dans les noms de constructeurs lorsqu'ils sont complexes (noms de templates, espaces nommés, etc.). On a une résolution pas si mal, mais il se peut que ça prenne des retouches ultérieures si on frappe des cas étranges qui nous ont échappé. Jason Merrill pense que ça recoupe deux cas semblables qui lui ont été assignés, soit 156 (fonctions de conversion) et 399 (destructeurs et « name » lookup).
Item 2071 : faut empêcher un typedef sans déclarateurs comme :
typedef struct X { }; // manque un nom entre } et ;
...qui est valide (on n'a rien pour l'empêcher) mais n'a pas de sens et devrait être diagnostiqué.
Jonathan Caves dit à la blague qu'interdire ceci briserait Windows
Plus sérieusement, ça ne fait rien mais faudrait l'interdire.
Item 2072 : il manque un peu de terminologie pour les paramètres avec valeur par défaut dans les méthodes génériques. À titre terminologique, semble qu'une méthode d'un template soit un temploïde.
Item 2073 : la restriction empêchant les exceptions d'allouer dynamiquement de la mémoire pose problème dans une situation multiprogrammée. Lever bad_alloc entre threads pourrait impliquer une forme d'allocation dynamique de mémoire. Richard Smith pense qu'on ne devrait pas contraindre les implémentations ici. On va y réfléchir, c'est une priorité 1. Hubert Tong pense que c'est une question de qualité d'implémentation, mais Aaron souligne qu'on pourrait causer des ennuis aux gens qui écrivent des allocateurs.
Item 2074 : une classe interne à une fonction générique est un type dépendant seulement si elle contient un objet de ce type, mais c'est obscur dans la pratique. On a des considérations d'alignement aussi : est-ce que le recours à une clause alignas rend un type dépendant dans un tel cas? Faudra préciser.
Jason Merrill dit qu'un des exemples contient un type dépendant et nécessite un typename :
template<int N>
struct Y
{
typedef int type;
};
template<int N>
void h()
{
struct alignas(N) X {};
Y<alignof(X)>::type z; // <-- ici
}
void i()
{ h<4>(); }
Item 2075 : question normative sur les conversions impliquant un tableau de N instance de x. La résolution proposée est adoptée. Ok.
Item 2076 : pas fin ici. On parle d'initialisation avec liste et de paramètres aux constructeurs. Richard Smith souligne que quand Clang a implémenté la résolution à 1467, ça a tout cassé chez eux. Ceci devenait incorrect :
struct A { A(int); };
struct B { B(A); };
B b{{0}}; // ambigu
Ceci aussi :
struct Params { int a; int b; };
class Foo
{
public:
Foo(Params);
};
Foo foo{{1, 2}}; // Foo(Params) ou Foo(Foo&&)
La résolution propose est de préciser une clause préférentielle pour lever l'ambiguïté. C'est une priorité 1.
Item 2077 : la résolution à 1604 a brisé ce qui suit :
struct A {};
struct B { operator const A() const; };
void f(A const&);
void f(A&&);
int main()
{
B a;
f(a); // choisit A&& et échoue
}
... et ceci :
std::vector<A> va;
B b;
va.push_back(b); // rejeté. Pas cool
La résolution ici n'est pas claire. Jason Merrill pense qu'on a corrigé celui-ci en ne considérant pas les conversions « maison » et réfère à 5.2.2 p.1 et à 8.5.3 p.5. Priorité 1.
Item 2078 : calvaire. L'exemple dit tout.
struct file_stat : ::stat { // the class
file_stat() : ::stat{} {} // the function
};
Jason Merrill dit, presque à la blague « oui, NAD, c'est vrai que ça peut donner des résultats surprenants, vous avez raison ». On s'entend là-dessus.
Item 2079 : bogue de grammaire avec [[ pour débuter une annotation si on veut permettre des fragments de code d'apparaître à cet endroit. John Spicer fait remarquer que c'est un bogue qui nous vient du futur. Aaron va le faire.
Item 2080 : exemple avec bris, mais Richard Smith va s'en occuper. C'est l'union vide qui ne fonctionne pas :
union U
{
int x = 0;
union { };
union
{
int z;
int y = 1; // error: initialization for second variant member of U
};
};
Hubert Tong y voit un problème; il reconnaît par contre que l'analyse proposée par la résolution est Ok.
Item 2081 : on en a discuté plus tôt cette semaine, indirectement.
Item 2082 : le texte du standard empêche ce qui suit, qui devrait être légal :
void foo(int a = decltype(a){});
Il y a des retouches à faire à quelques endroits pour régler ceci, par contre. Ça laisse transparaître une nuance entre used et odr-used.
Item 2083 : avec l'item 1741, on a des cas d'odr-used non-désirés qui briment des cas de constexpr qui seraient, autrement, légaux. C'est une priorité 1.
Item 2084 : ce qui suit est accepté par la plupart des compilateurs mais, au sens strict, n'est pas permis. On pense que le texte est trop restrictif avec NSDMI (Non-static Data Member Initialization). L'idée ici est que le constructeur par défaut de U serait =delete « normalement » mais il n'y a pas de bonne raison pour que ce soit le cas.
struct S
{
S();
};
union U
{
S s{};
} u;
Ici, le NSDMI est en fait l'équivalent d'un constructeur par défaut « maison ».
Item 2085 : on a un exemple qui n'est plus correct de constructeurs paramétriques avec paramètres munis de valeur par défaut. La règle est encore correcte, mais pour les méthodes autres que les constructeurs, alors faut retravailler l'exemple.
Item 2086 : nuances entre le odr-use d'une référence, son contexte d'utilisation et son initialisation. C'est très particulier comme exemple.
Item 2087 : un cas comme i<<0 pour i entier et négatif résulte en un comportement indéfini. Richard Smith pense que la règle est peut-être correcte telle quelle, et suggère que SG12 le regarde au moins. Priorité 1. Hubert Tong demande si les conversions arithmétiques impliquées pourraient être la source du comportement indéfini, mais Richard Smith indique que ces conversions sont implementation-defined.
Item 2088 : on a une règle ambiguë dans le cas de certaines conversions où plusieurs cas peuvent réussir. L'exemple est pervers :
template <typename T>
struct A
{
struct typeA { };
struct typeB { };
using convTyA = T (*const &&)(typename A<T>::typeA);
using convTyB = T (*const &)(typename A<T>::typeB);
operator convTyA();
operator convTyB();
};
template <typename T>
void foo(T (*const &&)(typename A<T>::typeA));
template <typename T>
int foo(T (*const &)(typename A<T>::typeB));
int main()
{
return foo<int>(A<int>());
}
C'est une priorité 1. Hubert Tong pense que ça rejoint l'item 1187.
Item
2089 : le standard dit que ceci est incorrect (ça devient operator+(int*,ptrdiff_t)
une fois résolu, et on n'a pas le droit de remplacer ça pour des raisons
que je vous laisse imaginer
) mais c'est
accepté par certaines implémentations :
struct Adaptor { Adaptor(int); };
struct List { };
void operator +(List &, Adaptor);
struct DataType
{
operator int *() const = delete;
operator List &() const;
};
struct Yea;
struct Nay { int theNaysHaveIt; };
template <typename T, typename U>
Yea addCheck(int, T &&t, U &&u, char (*)[sizeof(t + u, 0)] = 0);
template <typename T, typename U>
Nay addCheck(void *, T &&t, U &&u);
void test(DataType &data)
{ (void)sizeof(addCheck(0, data, 0.).theNaysHaveIt); }
Hubert Tong a une résolution à proposer, mais pas ce matin.
Item 2090 : question de types non-dépendants dans un contexte où ils deviennent, en apparence, dépendants. Richard Smith pense qu'il manque un mot dans la formulation. La définition de non-dependent member nous fait fouiller dans le standard. On sait ce qu'on veut; faudra l'écrire. On peut même carrément supprimer le paragraphe offensant; semble que ce soit le chemin choisi pour résoudre celle-ci.
Item 2091 : l'exemple est pervers. Je ne le voyais pas, mais :
template <int &> struct X;
template <int &N> void f(X<N>&);
int n;
void g(X<n> &x) { f(x); }
On utilise l'adresse de la variable globale n, techniquement, pas sa valeur, alors c'est Ok. Ici, on a une règle qui ne donne pas le bon résultat mais c'est corrigeable sur le plan terminologique. On est trop stricts. Faudra trouver une terminologie acceptable.
Brève pause, la dernière de la semaine pour moi. Je me prends un café et des grignotines, et je prépare le terrain pour ne pas déranger tout le monde quand je quitterai vu que les travaux seront encore en cours à ce moment.
Au retour, Hubert Tong dit s'être renseigné sur la covariance des coroutines. Pensez-y un peu… On génère du code côté bibliothèque alors ça a des conséquences divertissantes sur la générations du code.
Item 2092 : la plupart des implémentations refusent ceci :
template <class T = int> void foo(T*);
void test()
{
foo(0); // #1 valid?
foo<>(0); // #2 valid?
}
...mais il semble que ça devrait être raisonnable, au moins dans le cas #2. Clairement, foo<int>(0) est Ok. On a traité un cas très semblable plus tôt cette semaine.
Item 2093 : dans les clauses catch, on permet de qualifier les gestionnaires sur un pointeur ou une référence mais pas sur un pointeur ou une référence sur un membre d'instance. Cependant, plusieurs implémentations le supportent. Il manque un cas dans le standard, alors c'est une écriture mineure et de priorité 0 selon JM. Richard Smith se demande si ça peut briser l'ABI de gestion d'exceptions de quelqu'un. Jason Merrill simplifie : ça brise la compatibilité avec C. Hubert Tong explique ce que fait le compilateur d'IBM, et il lui faut aller vérifier des détails. Il se peut que le changement soit mineur.
Item 2094 : la clause 12.8 parag. 25.2 rend certains constructeurs non-triviaux avec un membre d'instance volatile, ce qui brise l'ABI du IA-64. On questionne la distinction entre type trivial et type trivialement copiable. Hubert Tong dit en riant que depuis qu'on utilise le mot trivial, plus rien qui utilise ce mot n'est trivial. Priorité 1.
Item 2095 : comment devrait-on capturer par copie une entité qui est une référence sur une rvalue vers une fonction? Ça peut intervenir avec des λ. Richard Smith pense que la rvalue reference devrait devenir une lvalue reference dans ce cas. Une retouche de formulation sera nécessaire.
Item
2096 : on serait trop contraignants pour la définition de type littéraux non-volatile, du moins dans le cas des union.
Richard Smith est d'accord : on pourrait avoir un
constexpr
avec un tel type, et cette écriture nous bloque.
Richard Smith fait
remarquer que si on a un type dont le constructeur n'initialise rien, il
pourrait être raisonnable de le considérer comme un type littéral,et il serait raisonnable de le construire
constexpr
dans ce cas. Mike rappelle qu'un constructeur
constexpr
doit initialiser tous les membres. Ok, on va demander d'initialiser « au moins un »
membre au lieu de « tous » les membres dans le cas des union.
Richard Smith dit qu'on
va faire semblant que les union anonymes n'existent
pas .
Item 2097 : semble qu'il soit présentement interdit d'écrire ceci :
auto bail = [] () [[noreturn]] { std ::exit(1); };
... à cause du [[noreturn]]. Ça semble pourtant raisonnable. Jonathan Caves dit que Gabriel Dos Reis aimerait que cette notation s'applique pour ses démarches de contrats. La position du [[noreturn]] ici est indicative qu'on voudra éventuellement utiliser les noms passés à la λ dans le contrat. On envoie ça à EWG..
Item 2098 : est-ce que uncaught_exceptions() est « par thread »? Le standard ne le dit pas, mais faudrait.
Item 2099 : bogue de grammaire ici (confusion entre initialisation par accolades et member-declarator).
... et je dois quitter. Snif parce que c'était diablement intéressant, mais j'ai vraiment hâte de revoir ma belle et mes chiboukis. Départ bien sympathique où on m'a dit souhaiter me revoir à Kona en octobre; c'est sûr que ça m'intéresse, mais ça va dépendre de facteurs externes à ma volonté.
C'est un chauffeur d'origine syrienne qui vient me cueillir au quartier général de Perceptive Software; il a fui Damas à cause de la guerre là-bas, et conduit une navette pour se faire des sous pendant ses études de réseautique (au sens matériel du terme). Dans son pays d'origine, il était comptable. Je me suis un peu fait prendre par le concierge de l'hôtel, qui a fait venir une navette plus luxueuse que ce que je me serais pris moi-même, mais l'écart n'est pas démesuré alors je fais avec (de toute manière, j'ai plus ou moins le choix si je veux aller à destination).
J'ai pu voir un peu le paysage, assez plat, vert et un peu agricole, mais pas trop (du moins sur mon trajet). Quelques vaches ici et là, quelques champs. On a passé sur un pont surplombant le fleuve Missouri, dans lequel le niveau de l'eau m'a semblé un peu bas.
À l'aéroport (plus de 100 Km du lieu de la rencontre), je ne me suis pas fait fouiller comme on le fait d'habitude, alors je présume que la craie est au moins en partie tombée de mon ordinateur dans le courant de la semaine. En attendant pour l'avion, la dame à côté de moi parle au téléphone et discute d'un meurtrier qui aurait assassiné trois personnes de ses mains; je pense qu'elle oeuvre dans une prison.
Je ne sais trop comment, mais deux petits oiseaux se sont introduits dans le Gateway où j'attends. Ils sautillent et gambadent allègrement en picorant les miettes de grignotines échappées par les passagères et les passagers.
Sur le vol de Kansas City vers Charlotte, mon compagnon était un médecin de la Floride, originaire de l'Ohio, qui a choisi un milieu plus chaud pour finir sa carrière. Il était allé à Kansas City pour une remise de diplômes des étudiant(e)s qu'il accompagne en internat parfois là où il travaille.
Charlotte semble être une petite ville, mais avec un gros aéroport. Semble
que cet aéroport serve de relais entre plusieurs villes. Les gens sont gentils;
ils viennent de souhaiter bonne fête des mères en avance, et avec enthousiasme.
Il est 19 h 30 et je n'avais pas dîné alors j'ai pris un sandwich (7,99 USD,
évidemment...) dans un truc nommé Baguette Dorée, où la bouffe semblait être de
type cuisine française (baguettes, croissants et tout le tralala; c'était très
bien, mais un peu gras), mais personne qui travaillait là ne parlait un mot de
français .
Ici, ils insistent beaucoup sur la vérification préalable des passeports alors
je vais aller me mettre en ligne pour que le tout soit vérifié... Pas question
de manquer le vol du retour!
À suivre...
Trucs que je retiens de la journée :
Trucs à retenir, d'ordre plus général :
À faire la prochaine fois, avant de me présenter à la rencontre :