La spécification du C++17 n'intègrera pas les concepts
Comprenez pourquoi la fonctionnalité a été retardée
Le 2016-04-19 21:46:43, par LittleWhite, Responsable 2D/3D/Jeux
La nouvelle spécification du C++, nommée C++17 approche à grands pas. Toutefois, la fonctionnalité des concepts n'intègrera pas la future spécification. Tom Honermann explique sur son blog les raisons faisant que c'était improbable, voire impossible.
Toutefois, avant de décrire ces raisons, rappelons ce que sont les concepts.
Prenons le concept suivant :
Celui-ci indique que n’importe quel type ayant un operator< et qui prend en paramètre deux objets et retournant un booléen sera considéré comme un LessThanComparable. Ensuite, il est possible d’utiliser le concept pour restreindre les types pouvant être passés à un template.
Le but des concepts est d'apporter une solution à un manque du C++. En effet, même s'il est possible de contourner le manque, il est impossible d'apporter une solution propre. Grâce aux concepts il devient possible :
Malgré tout l’intérêt que peuvent avoir les concepts, ceux-ci n'intégreront pas le prochain standard. En effet, plusieurs choses ne sont pas encore claires :
Toutefois, même si tous ces points avaient été réglés, Tom Honermann doute de l’intégration des concepts à la spécification du langage. En effet :
Même si ce constat est malheureux pour tout utilisateur du langage souhaitant les concepts au plus tôt, ces derniers devraient arriver dans la prochaine spécification. De plus, il y a de grandes chances pour que chaque compilateur propose une implémentation bien avant la complétion du futur standard. Finalement, ce retard permet d’affiner l’implémentation et ainsi, au comité de proposer une meilleure fonctionnalité.
Votre opinion
Aviez-vous déjà imaginé des cas d’utilisation pour les concepts ? Quels sont-ils ?
Quelles autres fonctionnalités du C++ attendez-vous ?
Source
Blog de Tom Honermann
IsoCPP
Toutefois, avant de décrire ces raisons, rappelons ce que sont les concepts.
Prenons le concept suivant :
Code c++ : |
1 2 3 | auto concept LessThanComparable<typename T> { bool operator<(T, T); } |
Le but des concepts est d'apporter une solution à un manque du C++. En effet, même s'il est possible de contourner le manque, il est impossible d'apporter une solution propre. Grâce aux concepts il devient possible :
- de contraindre les arguments d'une fonction sans pour autant désactiver la déduction de ceux-ci et sans gêner la meta arity des fonctions templates ainsi contraintes. Prenons l’exemple suivant :
Code c++ : template <class T> void f(T);
Code c++ : template <class T> void f(enable_if_t<my_trait_v<T>, T>);
Code c++ : template <class T> auto f(T) -> enable_if_t<my_trait_v<T>, void>;
Code c++ : template <class T, typename = enable_if_t<my_trait_v<T>>> void f(T);
Code c++ : void f(MyConcept);
- d’écrire plus facilement des surcharges tout en ayant des contraintes exclusives mutuellement. Il est souvent souhaité de pouvoir utiliser telle ou telle surcharge suivant certaines conditions sur les templates. Pour réussir, on pourrait écrire :
Code c++ : template <class T> void f(T, decltype(declval<T>().f())* = 0);
Code c++ : 1
2template <class T> void f(T) requires requires (T t) {t.f();}; template <class T> void f(T);
- d’écrire des contraintes aussi originales que nécessaires.
Malgré tout l’intérêt que peuvent avoir les concepts, ceux-ci n'intégreront pas le prochain standard. En effet, plusieurs choses ne sont pas encore claires :
- la spécification des concepts a été publiée le 15 novembre 2015, laissant peu de temps pour un retour efficace et fiable ;
- la seule implémentation est dans une version non publiée de GCC ;
- l’implémentation réalisée dans GCC a été réalisée par l’auteur de la spécification. Il n’y a donc pas eu d’avis externe sur la question de l’implémentation dans GCC ou dans les autres compilateurs ;
- seuls quelques projets utilisent les concepts, mais la spécification n’a pas été assez mise à l’épreuve dans des cas réels ;
- la spécification ne fournit pas de bibliothèque de définitions de concepts. Donc il n’est pas possible de savoir si l’écriture d’une telle bibliothèque est possible.
Toutefois, même si tous ces points avaient été réglés, Tom Honermann doute de l’intégration des concepts à la spécification du langage. En effet :
- les concepts apportent une nouvelle écriture pour les templates. Toutefois, une fonction template abrégée peut être identique à une fonction non template. Le type serait le seul indicateur pour savoir si la fonction est non template ou si elle est template :
Code c++ : void f(X x) {}
- la proposition définit une nouvelle syntaxe pour déclarer des templates respectant une contrainte :
Code c++ : C{A,B} void f(A a, B b);
- l’utilisation d’un concept nécessite de connaître comment il a été défini (fonction ou variable). Cela apporte confusion et est source d’erreurs ;
- les concepts sont attendus pour améliorer les messages d’erreur. Toutefois, l’utilisation erronée des concepts peut apporter des erreurs encore plus denses qu’à l’accoutumée liées à la surcharge des fonctions ;
- de nombreuses autres questions ont été soulevées et ne pourront être répondues qu’à travers des tentatives d’utilisation.
Même si ce constat est malheureux pour tout utilisateur du langage souhaitant les concepts au plus tôt, ces derniers devraient arriver dans la prochaine spécification. De plus, il y a de grandes chances pour que chaque compilateur propose une implémentation bien avant la complétion du futur standard. Finalement, ce retard permet d’affiner l’implémentation et ainsi, au comité de proposer une meilleure fonctionnalité.
Votre opinion
Source
Blog de Tom Honermann
IsoCPP
-
JolyLoicRédacteur/ModérateurJe suis assez en désaccord avec ce post qui présente pour moi une vue biaisée de la situation. Même si je n'étais pas à la dernière réunion, j'ai suivi d'assez près son contenu.la spécification des Concepts a été publiée le 15 novembre 2015, laissant peu de temps pour un retour efficace et fiablela seule implémentation est dans une version non publiée de GCC ;l’implémentation réalisée dans GCC a été réalisée par l’auteur de la spécification. Il n’y a donc pas eu d’avis externe sur la question de l’implémentation dans GCC ou dans les autres compilateurs ;seuls quelques projets utilisent les concepts, mais la spécification n’a pas été assez mise à l’épreuve dans des cas réels ;la spécification ne fournit pas de bibliothèque de définitions de concepts. Donc il n’est pas possible de savoir si l’écriture d’une telle bibliothèque est possible.les Concepts apportent une nouvelle écriture pour les templates. Toutefois, une fonction template abrégée peut être identique à une fonction non template. Le type serait le seul indicateur pour savoir si la fonction est non template ou si elle est template :
Il est courant en C++ de ne pas pouvoir dire ce qu'est un code sans connaître le contexte. Si je dis
Code : 1
2void f(double d) {}; f(2);
Et bien, dans le même ordre d'idée, si je vois void f(Container const &c), je ne peux pas savoir sans un peu de contexte si je définis une fonction ou un template, mais :
- Généralement, je connais le contexte, et je peux dire par exemple que Container est un concept, et donc que je définis un template, mais que f(vector<int> const &v) va définir une fonction.
- Le nom lui même doit m'aider
- Et la plupart du temps, je m'en moque totalement de savoir si une fonction est un template ou pas. J'ai sous la main en conteneur (sans savoir forcément son type exact, il est peut-être dans une variable auto initialisée par un retour de fonction, ou dans un argument template), je peux appeler f, c'est tout ce qui compte.les Concepts sont attendus pour améliorer les messages d’erreur. Toutefois, l’utilisation erronée des Concepts peut apporter des erreurs encore plus dense qu’à l’accoutumée lié à la surcharge des fonctions ;
- Ce n'est pas le cas le plus courant
- Lire une longue liste de surcharge reste au niveau sémantique de l'interface. Pour les gens ayant l'habitude de plonger dans l'implémentation, peut-être que c'est un peu plus long, mais pour les gens voulant rester au niveau de l'interface, c'est mieux !
- Je ne doute pas qu'avec le temps, les compilateurs pourront affiner les messages d'erreur liés aux concepts (par exemple, lister en premier parmi les surcharges celle qui était le plus proche de passer, et donc la plus susceptible d'être la bonne).de nombreuses autres questions ont été soulevées et ne pourront être répondues qu’à travers des tentatives d’utilisations.
Le problème, c'est qu'il y a un risque à ne pas livrer les concepts qui est pour moi plus important que le risque actuel de livrer des concepts imparfaits. Pour une autre vision sur le sujet, je vous propose de lire http://www.open-std.org/jtc1/sc22/wg...6/p0225r0.htmlle 19/04/2016 à 23:17 -
bacelarExpert éminent séniorPour avoir subit l'inverse, je peux te garantir que C puis C++ c'est la combo maudite pour faire de la merde sans le savoir.
Arrêtez de pendre vos lunettes de vieux cons, comment pouvez-vous regretter l'utilisation systématique de pointeurs nus dans un langage à exception ?
Les smart pointeurs, c'est irremplaçable, jusqu'à trouver encore mieux.
La normalisation du C++ a une démarche proche des framework progressifs, rendre simple les cas courants, faire en sorte que les cas courants couvre le plus de cas possible et faire en sorte que les cas rares et complexes soient
Et oui, la programmation générique (template simple), le code fonctionnel à base de lambda, la programmation parallèle ou concurrente sont des cas maintenant des plus courants ou en passe de l'être. Et que les normalisateurs me tracent une autoroute pour m'en servir le plus simplement du monde, je leur en suis profondément reconnaissant.
Moi, je ne maitrise pas le C++, même après plus de 20ans d'utilisation, mais tant que j'arrive à faire ce que je veux et qu'en cherchant un peu je trouve de super trucs qui me simplifient la vie, bibliothèque ou nouvelles normes, je suis content.
Avoir l'illusion de maitriser un machin et bien plus dévastatrice que de savoir qu'on ne sait rien.
Désolé les gars, mais les seules personnes qui peuvent dire que le C++ est trop compliqué, c'est les vrais débutants, pas ceux qui se paluchent les intrinsics des compilateurs en assembleur x64 ou Itanium.le 20/04/2016 à 14:41 -
Luc HermitteExpert éminent séniorA propos de C plus léger et plus rapide, je renvoie aux présentations que j'avais évoquées ici: http://www.developpez.net/forums/d32...r/#post8505022
Sinon, AMA, trouver ce type de code plus simple à comprendre est une illusion dont on se berce.
Ceci n'est que le code "bas-niveau". Comme je disais, imaginez maintenant un filtre qui va extraire un bandeau de l'image (et donc construire un objet sous-image), et parser tous les pixels pour calculer des statistiques ou faire autre chose.
Il faut prévoir une nouvelle ressource allouée : le bandeau (et donc sa libération en cas d'erreur sur get_pixel), et une boucle qui va itérer sur chaque pixel. Ce cas est encore simple. Cela devient encore plus compliqué sur des calculs de corrélation où l'on extrait 2 bandeaux qui sont parcourus simultanément).
Déjà qu'un code C correct est complexe: cf le code en bas à gauche dans http://alexandre-laurent.developpez....ou-exceptions/, si en plus on rajoute de la programmation défensive (pour résister aux erreurs de programmation), le code devient encore plus complexe.
En C, il aurait été plus propre de rendre la classe/structure image complètement cachée (comme FILE), histoire de retirer le code qui vérifie à chaque fois si tout ce qui est reçu est dans un état correct. C'est à dire: simuler des invariants assurés par encapsulation+constructeur.
Mon point, est que le C++ offre de "nouveaux" (pour le coup, ceux là vieux) moyen pour simplifier notre code. On ne va quand même pas refuser des sucres syntaxiques sous prétexte que c'est une syntaxe alternative à une que nous avons déjà.le 21/04/2016 à 10:08 -
Luc HermitteExpert éminent séniorSi tu prends fasta, et regarde les codes, tu verras une énorme différence: "#pragma OMP". On ne compare pas les mêmes choses. Pour les arbres binaires, on n'utilise pas le même COTS. Pareil pour la lib de regex dans le suivant. Il est difficile de comparer quand autant d'éléments changent, et qu'il ne s'agit pas juste de remplacer des structures par des classes (pour mieux contrôler les invariants et remplacer de nombreux cas d'utilisation du débuggeur par le compilateur dans l'assistance à la maintenance de l'application), et prendre un peu de template par-ci par-là.
La part du C dans l'embarqué est du à deux raisons : la tradition et les aprioris (je ne sais plus si c'est le sujet de la 1ère ou de la 2nd présentation de Dan Saks lors du dernier code::dive), et au problème de support par des compilateurs maintenus par les fournisseurs de matos (qui n'ont pas forcément envie, ni les moyens, de fournir des compilateurs plus complexes comprenant le C++). Et aussi on a facilement des problèmes d'ABI. Si tu veux faire dans l'ad hominem, je pense pouvoir dire que Dan Saks est un développeur sérieux qui bosse dans l'embarqué. Et pourtant il se fait l'avocat du C++ depuis très longtemps.
Qui plus est, si une construction propre au C++ n'est pas acceptable, il suffit d'utiliser l'équivalente du C. Un code C compilé avec un compilateur C++ n'a pas à être plus lent -- cf la video de l'autre intervenant dont le nom m'échappe. A partir de là, tu utilises juste ce que tu veux du C++. Et même en embarqué (moyennant la disponibilité d'un compilateur), tu ne dois pas observer de dégradations.
En effet, je sens un problème de communication. Désolé.
La simplicité de compréhension ne se borne pas au cas local. Il est vrai que la simplicité que je critique ne se limite pas à ces deux fonctions. Elle englobe tout le produit de traitement d'image. Quand je vois un tel code (en général, je commence par la couche haute avant de descendre pour savoir si on n'aurait pas des fuites de ressources), je dois accorder plus d'attention pour comprendre ce qu'il fait. Cela vaut aussi bien pour la maintenance (a-t-on oublié des cas?), que pour son utilisation.
Et justement, son utilisation est à 100% embarquée dans du code qui continue à faire la même chose. Ce code que j'ai montré est le plus simple car tout au fond des appels. Quand on maintiens les couches au dessus, (qui suivent la même politique, détail très important), c'est vite l'enfer car il faut vérifier les cas remontés par cette partie, plus ceux remontés par les autres parties employées. Là, on a constitué une opération plus ou moins simple à appréhender, et on recommence en l'utilisant à un niveau supérieur. Mon expérience est que la complexité explose et qu'il y a des potentiels de fuites assez importants si un erreur de programmation est bel et bien détectée.
Le problème de ce type de code est le parasitage par la programmation défensive qui rend vite tout plus compliqué.
get_size() fait du défensif. Il est localement assez simple. get_pixel fait du défensif, il serait presque simple, sauf qu'il appelle une fonction qui elle-même fait du défensif, ce qui implique une complexification du code de get_pixel. get_region fait du défensif à son niveau et appelle d'autres fonctions qui font du défensif (get_size(), get_pixel()).
S'il y a des raccourcis que je prends, c'est lié au fait que j'affirme que grâce à class+private+constructeur, j'ai moyen d'éliminer une part non négligeable du défensif, ce qui permet d'avoir des fonctions plus simples à chaque niveau. Avec les exceptions, on peut encore aller plus loin : si on a un objet bandeau, alors il ne peut que être valide, et alors nul besoin de tester des choses assurées par construction du programme.le 21/04/2016 à 12:56 -
JolyLoicRédacteur/ModérateurIl n'y a pas de bonne raison pour ça, sauf que les compilateurs C++ sont moins présents, moins robustes et moins certifiés que les compilateurs C. Mais ça n'empêche pas des entreprises travaillant dans l'embarqué d'utiliser du C++ sur des aspects safety-critical. On peut citer Lockheed Martin, qui a par exemple publié des règles de codage en C++ (http://www.stroustrup.com/JSF-AV-rules.pdf), ou le Mars Rover qui contient à la fois de C et du C++ (et on aura du mal à me faire croire que ces machines ont de la mémoire et de l'énergie à revendre...).le 22/04/2016 à 0:02
-
MédinocExpert éminent séniorC++ est ce qui permet de ne payer que pour ce qu'on utilise, tant en place mémoire qu'en vitesse.
Et le unique_ptr<> est l'une des fonctionnalités les moins chères et les plus utiles du C++: Un pointeur transférable, mais garanti n'avoir qu'un seul "propriétaire", et gérant la désallocation automatiquement.
C'est le meilleur moyen d'écrire du code exception-safe, absolument indispensable.le 26/04/2016 à 14:52 -
Luc HermitteExpert éminent séniorTu compares des choux et des carottes.
Si tu as besoin de construire ton objet (histoire de positionner l'invariant "est dans un état utilisable", alors il te faut le faire. New est donc équivalent à malloc + construction
Si tu n'en a pas besoin (i.e. parce que l'objet est un POD -- je simplifie), alors new ne fera rien de plus qu'un malloc.
Bref, tu paies avec new la même chose que ce que tu dois payer avec malloc. Si maintenant tu t'amuses à dé-PODifier tes agrégats de données dépourvues d'invariants pour le plaisir de faire semblant de faire de l'OO ... le prix inutile, c'est pas la faute de new.
S'il y a critique à faire, potentiellement, new pourrait appeler malloc de façon mal inlinée et pas une tierce fonction interne sur laquelle malloc repose également. OK, on paierait quelques petits cycles de plus à chaque allocation dans une telle situation.le 27/04/2016 à 12:04 -
BouskRédacteur/Modérateurmalloc est tellement différent de new qu'il peut être utilisé pour le surcharger et qu'il est utilisé en interne dans certaines implémentations
http://en.cppreference.com/w/cpp/mem...w/operator_new
http://stackoverflow.com/questions/2...oc-free#240308
En fait la seule différence c'est vraiment l'appel au constructeur de la part de new après l'allocation. Donc si t'as des constructeurs triviaux/POD (ce qui est le cas en C où tu ne peux pas avoir autre chose), ça change strictement rien. Si t'as un constructeur non trivial, ben tu le payes.le 28/04/2016 à 12:41 -
ternelExpert éminent séniorauto, car tant qu'a faire, utiliser la déduction de type.
Il aurait pu écrire bool concept LessThanComparable<typename T> {bool operator<(T, T);}.
Cela dit, je crois que la syntaxe proposée était plutot:
Code : 1
2
3
4template<typename T> concept bool LessThanComparable = requires(T a, T b) { { a < b } -> bool; };
Regardez par exemple sur cppreference.com pour la proposition actuelle de Concepts Technical Specification ISO/IEC TS 19217:2015.
La proposition est suffisamment intéressante à mon gout, elle permet de spécifier des attentes plus complexes, telles que "dispose de begin et end", "dispose de size()", ou encore est un appelable avec tels arguments.
Je dois dire que j'ai récemment écrit une bibliothèque de manipulation de fonctions mathématiques (somme, produit, composition, etc) et qu'un concept tel que callable_as<arg1, arg2> m'aurait grandement arrangé.
La moitié du code de cette bibliothèque consiste précisément à me donner cette capacité, au prix d'appel de deux classes de traits (et sept spécialisations chacune), de pleins de static_assert.
Avec les concepts, je n'aurai eu qu'a écrire une template de concept, et c'est tout.le 20/04/2016 à 10:37 -
EhonnMembre chevronnéOui, le langage devient de plus en plus complet / complexe.
Mais du coup son utilisation devient plus simple (et l'apprentissage de son utilisation aussi).
Les débutants n'ont pas / plus besoin d'apprendre les choses compliquées.
Pour donner un exemple avec les concepts : l'apprentissage et l'utilisation des concepts est beaucoup plus simple que celles de SFINAE.
Certains programmes qui étaient compliqués peuvent être réécrits en programmes relativement simples (même pour les débutants).le 20/04/2016 à 13:52