Vous êtes nouveau sur Developpez.com ? Créez votre compte ou connectez-vous afin de pouvoir participer !

Vous devez avoir un compte Developpez.com et être connecté pour pouvoir participer aux discussions.

Vous n'avez pas encore de compte Developpez.com ? Créez-en un en quelques instants, c'est entièrement gratuit !

Si vous disposez déjà d'un compte et qu'il est bien activé, connectez-vous à l'aide du formulaire ci-dessous.

Identifiez-vous
Identifiant
Mot de passe
Mot de passe oublié ?
Créer un compte

L'inscription est gratuite et ne vous prendra que quelques instants !

Je m'inscris !

C++ doit-il moderniser son mécanisme d'inclusion ?
CoderGears estime que c'est nécessaire pour qu'il devienne un langage vraiment moderne

Le , par Amine Horseman

0PARTAGES

6  8 
Il y a 3 ans, les développeurs C++ avaient accueilli la nouvelle standardisation du langage avec beaucoup d'enthousiasme. Avec ce nouveau standard, plusieurs fonctionnalités avaient été ajoutées ou mises à jour pour faire du C++ un langage plus moderne et efficace, tout en conservant les anciens avantages tels que l'accès bas niveau à la mémoire par exemple.

Cependant, d'autres améliorations ont été reportées, car il aura fallu quand même 13 ans pour finir cette normalisation!
Une des améliorations possibles, qui restent toujours intéressantes, c'est de repenser le système d'inclusion de fichiers. C'est en tout cas ce que propose l’équipe CoderGears dans un billet de blog. Ils sont même allés plus loin en proclamant que « le C++ doit moderniser son mécanisme d’inclusion pour devenir vraiment un langage moderne ».

Jusqu'à présent, lorsqu'un fichier d'entête est inclus grâce à la directive #include, le préprocesseur analyse le contenu du fichier ainsi que tous les autres fichiers qu'il inclut lui-même. Et ceci est répété à chaque #include rencontré. Ce qui conduit parfois à un grand volume de travail redondant lorsque deux fichiers incluent la même bibliothèque, et que celle-ci n’est pas entourée de #ifndef et #endif. Mais ce n'est pas le seul problème, car lors de l’analyse, si une macro par exemple porte le même nom qu'un autre composant dans cette bibliothèque, c'est toute la compilation qui échoue. « Des problèmes se produisent dans des projets réels lorsque les entêtes pour deux bibliothèques différentes interagissent en raison de collisions macro, et les utilisateurs sont obligés de réorganiser les directives #include ou introduire des #undef pour briser le lien », explique CoderGears.

Certains programmeurs, avec le temps, ont développé de nouvelles habitudes pour éviter ça. Dans la documentation de Clang, on peut lire que « les programmeurs C++ ont adopté un certain nombre de conventions pour travailler autour de la fragilité du modèle de préprocesseur […] Les noms de macros sont écrits avec de LONG_PREFIXES_EN_MAJUSCULES pour éviter les collisions, et certains développeurs de bibliothèques utilisent même un double tiret dans les en-têtes pour éviter les collisions avec des noms "normaux"». Cette pratique, qui ne respecte pas les conventions du langage, est apparemment utilisée par certains pour contourner le problème.

Heureusement, une solution existe depuis la proposition de la norme C++ox, mais elle n'a pas été implémentée et a été retardée pour la norme C++11, puis renvoyée une nouvelle fois à une prochaine standardisation du langage de programmation.

Cette solution devrait alléger grandement le code des entêtes ainsi que leur traitement, puisqu'elle propose de ne plus inclure les fichiers d'une bibliothèque séparément. Par exemple : au lieu de charger les fichiers de la bibliothèque standard du C++ un à un, un simple import std suffira. L'entête gagnera donc beaucoup en clarté. De plus, cela inclura directement le fichier binaire de la bibliothèque (en tant que module déjà prêt), ce qui évitera au préprocesseur de le réanalyser à chaque fois, et évitera en même temps de tomber sur des conflits lors de la compilation.

« L’introduction de modules en C ++ ne sera pas une tâche facile », avoue l’équipe CoderGears. « Nous espérons que ce ne sera pas comme jisgaw (le système modulaire de Java) qui a été reportée de nombreuses années ».

Source : CoderGears Blog, Clang Documentation

Et vous?

Partagez-vous le point de vue de l’équipe de CoderGears ?

Une erreur dans cette actualité ? Signalez-le nous !

Avatar de Uther
Expert éminent sénior https://www.developpez.com
Le 21/11/2014 à 18:17
Article bizarre qui enfonce des portes grandes ouvertes.

Toute personne qui a fait un minimum de C++ sait bien qu'on doit moderniser son mécanisme d'inclusion. Tout le monde le sait, tout le monde l'attend avec impatience a chaque nouvelle évolution du langage, mais on comprend également qu'un tel changement n'est pas anodin et qu'il prend du temps.
9  0 
Avatar de Flob90
Membre expert https://www.developpez.com
Le 21/11/2014 à 19:29
Bonjour,

Tout d'abord la proposition actuel : http://www.open-std.org/JTC1/SC22/WG...2014/n4214.pdf et la précédente : http://www.open-std.org/JTC1/SC22/WG...2012/n3347.pdf (je ne l'ai pas encore lu, donc je me base plus sur le premier).

@Neckara: Il n'est pas question de "binaire" en réalité, donc aucun problème pour fonctions inlines et templates. De plus dans l'idée, les modules ne doivent pas changer les règles actuelles.

Pour les import, ne t'inquiète pas, des sous-modules sont bien prévus, on ne va pas importer std en entier à chaque fois.

En effet le but est bien de changer en profondeur le système de compilation tout en permettant d'encore utiliser l'ancien. Le système actuel est basé sur du copier/coller, l'intérêt des modules c'est de dire : "voila, d'un côté les éléments suivants de cette unité sont importables par d'autre unité au sein de tel module, et de l'autre j'aimerais bien utilisé les éléments de tel module". On évite ainsi les copier/coller. Niveau implémentation, il faudrait regarder ce que clang avait proposé, mais je suppose que ça implique de générer des données supplémentaire (après le passage du préprocesseur) qui permet de savoir ce qu'il y a dans chaque module.

Niveau avantage, la limitation des violations de l'ODR (ie les erreurs "truc déjà définie" est celle qui m'attire le plus personnellement.
6  0 
Avatar de JolyLoic
Rédacteur/Modérateur https://www.developpez.com
Le 21/11/2014 à 21:57
Citation Envoyé par Neckara Voir le message
Mais ce que j'attends avec plus d'impatience, c'est une normalisation de tout ce qui est "plugin" et chargement dynamique de bibliothèques dynamiques.
Je n'ai pas vu beaucoup de discussions autour de ce sujet, probablement car :
- Ça demande énormément de travail de spécification difficile à écrire, en plus de la notion de modules (car la norme est aujourd'hui pudiquement silencieuse sur tout un tas de sujets qu'il faudrait expliciter pour définir ça)
- Il existe des pratiques un peu partout, mais qui sont divergentes sur tout un tas de points. Ce qui laisse présager des guerres de religion.

Je pense que le point de vue actuel est "attendons d'avoir les modules dans une version un peu près stable, et à ce moment là on regardera comment faire des modules chargés dynamiquement".

Citation Envoyé par Amine Horseman Voir le message
Heureusement, une solution existe depuis la proposition de la norme C++ox, mais elle n'a pas été implémentée et a été retardée pour la norme C++11, puis renvoyée une nouvelle fois à une prochaine standardisation du langage de programmation.
C'est un peu optimiste comme vision... Il n'y a pas vraiment une solution dans la norme C++0x, juste une proposition d'évolution comme tant d'autres, qu'une personne a tenté d'implémenter pour Clang afin de la valider un peu. Ce qui est récent sur le sujet, c'est que d'autre compilateurs que Clang ont manifesté un intérêt pour le sujet (les dernières propositions sont de Microsoft), ce qui j'espère va fortement booster ce sujet qui était un peu au point mort après une première implémentation Clang.

Pour ce qui est du format de fichier, rien n'est précisé dans les dernières propositions. Ce sera probablement un format binaire (pour des raisons de performances), mais pas exécutable. Il n'est pas prévu d'imposer une compatibilité entre compilateurs sur ce format, même si on peut imaginer que des compilateurs se voulant compatibles (gcc et clang) vont avoir intérêt à se mettre d'accord pour un format commun.

On peut le voir comme un super PCH, qui apporterait une grande nouveauté par rapport aux PCH : La composabilité. Aujourd'hui, on fait un PCH pour un programme qui utilise libA et libB. Le but est de pouvoir faire un PCH pour libA, un autre pour libB, et de pouvoir dire qu'on utilise les deux pour notre programme.
5  0 
Avatar de JolyLoic
Rédacteur/Modérateur https://www.developpez.com
Le 28/11/2014 à 9:59
On reproche globalement 3 choses au #include :
- C'est compliqué à enseigner (cette histoire d'inclusion textuelle de déclaration pour avoir moyen de se linker avec les définitions n'est pas si simple)
- Ce n'est pas composable : Il n'est pas possible de dire : "voilà une description de l'interface de ma bibliothèque, utilise là sans soucis", car suivant l'ordre d'inclusion, une macro dans un header peut, volontairement ou pas, déteindre sur un autre header. Exemple concret : Windows a défini tout un tas de macro pour que ses fonctions manipulant des chaînes existe en 2 versions, une finissant par A, une par W, et qu'on bascule de manière transparente de l'une à l'autre selon des options de compilation. J'avais une classe avec une fonction membre nommée par exemple Toto, mais ce nom correspondait à une fonction de l'API windows, et donc si j'incluais <windows.h> avant mon code, une macro faisait que ma fonction était silencieusement renommée TotoW. Si j'incluais directement ma classe, elle s'appelait bien Toto. Et donc, ce qui pour moi n'était qu'une fonction était en fait un truc schizophrène, qui ne marchait pas au moment du link.
- Ce n'est pas efficace (par exemple, si 50 fichiers de code source #incluent <windows.h>, qui récursivement fait plusieurs centaines de milliers de lignes, et bien le compilateur va devoir analyser 50 fois ces centaines de millier de lignes). Et ce n'est pas possible de totalement rationaliser ça, en ne les ayant analysées qu'une seule fois et en réutilisant le résultat, car la manière d'analyser ces lignes dépend de tous les headers qui ont été inclus auparavant (on ne sais jamais, si une macro modifiait le sens d'un identifieur).

Les modules ont pour objectif de définir une frontière où les macros ne se propagent pas (les détails restent à définir), afin de permettre une définition plus clair et moins corruptible de l'interface, tout en améliorant les possibilités d'optimisation des compilateurs.
4  0 
Avatar de Neckara
Inactif https://www.developpez.com
Le 21/11/2014 à 18:43
Bonjour,

Inclure directement le binaire de la bibliothèque.
Et on fait comment pour les macro, les templates ou les fonctions inlines .

Pourquoi ne pas tout simplement utiliser des fichiers d'en-têtes "pré-compilés" ?

lorsque deux fichiers incluent la même bibliothèque, et que celle-ci n’est pas entourée de #ifndef et #endif.
Je doute que ceci soit si fréquent que ça, et il existe aussi des solutions (pas géniales, mais qui existent).

Après il existe bien #pragma include_once qui ne fait pas parti de la norme mais, il me semble qu'il marche pas trop mal.

un simple import std suffira
Importer tout std ? Pour chaque fichier source utilisant la std ? Ceci me semble un peu lourd.
Importer uniquement les modules dont on a besoin ?
Code : Sélectionner tout
1
2
#import std.iostream
#import std.vector
Je ne vois pas trop ce que ceci apporterait de plus que :
Code : Sélectionner tout
1
2
#include <iostream>
#include <vector>
Je n'aime pas trop le système proposé, je pense qu'elle induit trop de changements sur les étapes de la chaîne de compilation.
Alors que je pense qu'on pourrait trouver des solutions plus simples et plus efficaces.
3  1 
Avatar de gl
Rédacteur https://www.developpez.com
Le 24/11/2014 à 19:04
Citation Envoyé par Flob90 Voir le message
De plus entre permettre une grammaire de plus pour un opérateur déjà existant et intégrer une nouvelle fonctionnalité, il y a un monde, la comparaison module/sizeof me semble totalement hors sujet.
D'autant que "le sizeof sans parenthèse" n'est pas une nouvelle syntaxe mais un comportement historique hérité du C (et plutôt logique vu qu'il s'agit d'un opérateur et qu'il s'utilise ainsi comme tous les autres opérateurs - dont return -). Bref, il n'y a pas eu de travail du comité la-dessus.
2  0 
Avatar de Neckara
Inactif https://www.developpez.com
Le 21/11/2014 à 20:53
D'accord, donc on ne fait pas réellement "d'inclusion" (au sens copié/collé) et il n'est pas questions du fichier "binaire de la bibliothèques".

En lisant l'actualité, je comprenais "on prend le .so/.dll et on le 'copie-colle'"... Je ne dirais rien, mais j'en pense pas moins .

Si je comprend bien, à partir des fichiers .cpp, on va générer une sorte de "header précompilé" qui ne sera plus utilisée par le préprocesseur (pour copier/coller le code) mais après.

Par contre, est-ce qu'il est prévu que ces "header précompilé" soient distribuées à l'intérieur des .so/.dll ou ils pourront encore être distribués séparément ?

C'est une idée intéressante, à voir ce que cela donnera. Mais ce que j'attends avec plus d'impatience, c'est une normalisation de tout ce qui est "plugin" et chargement dynamique de bibliothèques dynamiques.
1  0 
Avatar de
https://www.developpez.com
Le 21/11/2014 à 21:09
Mais ce que j'attends avec plus d'impatience, c'est une normalisation de tout ce qui est "plugin" et chargement dynamique de bibliothèques dynamiques.


Il existe divers façon de le faire pour linux, mac/osX et windows mais, en effet, ce n'est pas encore normalisé. :/

En attendant il faut faire sans, mais, je me demande si un jour il y aura une méthode standard.

PS : Les "include" pour le moment ça ne me gêne pas tellement, à part peut être, les problèmes dû à la double définition des macros cité plus haut.
1  0 
Avatar de Neckara
Inactif https://www.developpez.com
Le 21/11/2014 à 21:16
Je pensais à quelque chose de plus orienté objet et sans casts de void * .
Quelque chose qui permettrait aussi d'utiliser les exceptions.

Une gestion de la mémoire pensée avec les plugins (pour ne pas faire planter les std::function/pointeurs intelligents), etc.
1  0 
Avatar de Flob90
Membre expert https://www.developpez.com
Le 24/11/2014 à 16:25
Citation Envoyé par octal Voir le message
la solution radicale au problème des includes a déjà été proposée à maintes reprise par Apple et ça n'a jamais été retenu (http://llvm.org/devmtg/2012-11/Gregor-Modules.pdf). Il s'agit de l'utilisation des "module". Le mécanisme date des années où un certain Monsieur Wirth a proposé le mécanisme de module dans Modula-2 puis Oberon. Il ne s'agit pas d'utiliser le binaire, il s'agit tout simplement de charger et analyser le module une seule fois, et d'avoir un référentiel de modules chargés. Chaque nouvelle inclusion ne fera que référencer le module en question (donc plus d'analyse redondante et plus de recompilation). Cela marche très bien pour Modula-2 et Oberon, mais l'organisme qui gère la standardisation du langage C++ est monopolisé par les intégristes tout aussi bornés les uns que les autres. On préfère rajouter des bizarreries comme la possiblité d'utiliser l'opérateur sizeof() sans les parenthèses quand il est appliqué à une variable, ce qui ne fait que rendre le langage encore plus m......que qu'autre chose, au lieu de reprendre les mécanismes des autres langages qui pourraient rendre le C++ bien plus clean et plus productif.
Il ne faut pas dire tout et n'importe quoi ... L'idée des modules est bien retenu par le comité de standardisation, d'où la mise en place d'un groupe de travail sur cette thématique. Le comité sait regarder vers les autres quand il s'agit de fournir de nouvelles fonctionnalités, les lambdas en sont un bel exemple. De plus entre permettre une grammaire de plus pour un opérateur déjà existant et intégrer une nouvelle fonctionnalité, il y a un monde, la comparaison module/sizeof me semble totalement hors sujet. (voir message suivant de gl)

Edit: Edition pour renvoyer au message de gl.
2  1