Supposons que j'ai trois objets compilés, tous produits par le même compilateur / version :
- A a été compilé avec le standard C ++ 11
- B a été compilé avec le standard C ++ 14
- C a été compilé avec le standard C ++ 17
Pour plus de simplicité, supposons que tous les en-têtes ont été écrits en C ++ 11, en utilisant uniquement des constructions dont la sémantique n'a pas changé entre les trois versions standard , et ainsi toutes les interdépendances ont été correctement exprimées avec l'inclusion d'en-tête et le compilateur n'a pas objecté.
Quelles combinaisons de ces objets est-il et n'est-il pas sûr de se lier à un seul binaire? Pourquoi?
EDIT: les réponses couvrant les principaux compilateurs (par exemple gcc, clang, vs ++) sont les bienvenues
std::string
implémentation active dans libstdc ++ est indépendante du-std
mode utilisé . C'est une propriété importante, précisément pour supporter des situations comme les PO. Vous pouvez utiliser le nouveaustd::string
dans le code C ++ 03, et vous pouvez utiliser l'ancienstd::string
dans le code C ++ 11 (voir le lien dans le commentaire ultérieur de Matteo).Réponses:
Pour GCC, il est sûr de lier ensemble toute combinaison d'objets A, B et C. S'ils sont tous construits avec la même version, ils sont compatibles ABI, la version standard (c'est-à-dire l'
-std
option) ne fait aucune différence.Pourquoi? Parce que c'est une propriété importante de notre implémentation que nous nous efforçons de garantir.
Là où vous avez des problèmes, c'est si vous liez des objets compilés avec différentes versions de GCC et que vous avez utilisé des fonctionnalités instables d'un nouveau standard C ++ avant que le support de GCC pour ce standard ne soit complet. Par exemple, si vous compilez un objet en utilisant GCC 4.9 et
-std=c++11
et un autre objet avec GCC 5 et-std=c++11
vous aurez des problèmes. Le support C ++ 11 était expérimental dans GCC 4.x, et il y avait donc des changements incompatibles entre les versions GCC 4.9 et 5 des fonctionnalités C ++ 11. De même, si vous compilez un objet avec GCC 7 et-std=c++17
et un autre objet avec GCC 8 et que-std=c++17
vous aurez des problèmes, car le support de C ++ 17 dans GCC 7 et 8 est encore expérimental et en évolution.D'autre part, toute combinaison des objets suivants fonctionnera (bien que voir la note ci-dessous sur la
libstdc++.so
version):-std=c++03
-std=c++11
-std=c++17
Cela est dû au fait que la prise en charge de C ++ 03 est stable dans les trois versions de compilateur utilisées et que les composants C ++ 03 sont donc compatibles entre tous les objets. Le support C ++ 11 est stable depuis GCC 5, mais l'objet D n'utilise aucune fonctionnalité C ++ 11, et les objets E et F utilisent tous deux des versions où le support C ++ 11 est stable. La prise en charge de C ++ 17 n'est stable dans aucune des versions de compilateur utilisées, mais seul l'objet F utilise des fonctionnalités C ++ 17 et il n'y a donc pas de problème de compatibilité avec les deux autres objets (les seules fonctionnalités qu'ils partagent proviennent de C ++ 03 ou C ++ 11, et les versions utilisées rendent ces parties OK). Si vous vouliez plus tard compiler un quatrième objet, G, en utilisant GCC 8 et que
-std=c++17
vous auriez besoin de recompiler F avec la même version (ou pas de lien vers F) car les symboles C ++ 17 dans F et G sont incompatibles.La seule mise en garde pour la compatibilité décrite ci-dessus entre D, E et F est que votre programme doit utiliser la
libstdc++.so
bibliothèque partagée de GCC 7 (ou version ultérieure). Comme l'objet F a été compilé avec GCC 7, vous devez utiliser la bibliothèque partagée de cette version, car la compilation de n'importe quelle partie du programme avec GCC 7 pourrait introduire des dépendances sur des symboles qui ne sont pas présents danslibstdc++.so
GCC 4.9 ou GCC 5. De même, si vous avez lié à l'objet G, construit avec GCC 8, vous devrez utiliser lelibstdc++.so
de GCC 8 pour vous assurer que tous les symboles nécessaires à G sont trouvés. La règle simple est de s'assurer que la bibliothèque partagée que le programme utilise au moment de l'exécution est au moins aussi nouvelle que la version utilisée pour compiler l'un des objets.Une autre mise en garde lors de l'utilisation de GCC, déjà mentionnée dans les commentaires de votre question, est que depuis GCC 5, il existe deux implémentations de
std::string
disponibles dans libstdc ++. Les deux implémentations ne sont pas compatibles avec les liens (elles ont des noms mutilés différents, ne peuvent donc pas être liées ensemble) mais peuvent coexister dans le même binaire (elles ont des noms mutilés différents, donc ne pas entrer en conflit si un objet utilisestd::string
et le autres utilisationsstd::__cxx11::string
). Si vos objets utilisent,std::string
ils doivent généralement tous être compilés avec la même implémentation de chaîne. Compilez avec-D_GLIBCXX_USE_CXX11_ABI=0
pour sélectionner l'gcc4-compatible
implémentation d' origine , ou-D_GLIBCXX_USE_CXX11_ABI=1
pour sélectionner la nouvellecxx11
implémentation (ne vous laissez pas berner par le nom, il peut aussi être utilisé en C ++ 03, il s'appellecxx11
car il est conforme aux exigences C ++ 11). L'implémentation par défaut dépend de la configuration de GCC, mais la valeur par défaut peut toujours être remplacée au moment de la compilation avec la macro.la source
Il y a deux parties à la réponse. Compatibilité au niveau du compilateur et compatibilité au niveau de l'éditeur de liens. Commençons par le premier.
L'utilisation du même compilateur signifie que le même en-tête de bibliothèque standard et les mêmes fichiers source (les onces associés au compilateur) seront utilisés quel que soit le standard C ++ cible. Par conséquent, les fichiers d'en-tête de la bibliothèque standard sont écrits pour être compatibles avec toutes les versions C ++ prises en charge par le compilateur.
Cela dit, si les options du compilateur utilisées pour compiler une unité de traduction spécifient une norme C ++ particulière, alors toutes les fonctionnalités qui ne sont disponibles que dans les normes plus récentes ne devraient pas être accessibles. Ceci est fait en utilisant la
__cplusplus
directive. Voir le fichier source vectoriel pour un exemple intéressant de la façon dont il est utilisé. De même, le compilateur rejettera toutes les fonctionnalités syntaxiques offertes par les nouvelles versions de la norme.Tout cela signifie que votre hypothèse ne peut s'appliquer qu'aux fichiers d'en-tête que vous avez écrits. Ces fichiers d'en-tête peuvent provoquer des incompatibilités lorsqu'ils sont inclus dans différentes unités de traduction ciblant différentes normes C ++. Ceci est discuté dans l'annexe C de la norme C ++. Il y a 4 articles, je ne parlerai que du premier et je mentionnerai brièvement le reste.
C.3.1 Clause 2: conventions lexicales
Les guillemets simples délimitent un littéral de caractère en C ++ 11, alors qu'ils sont des séparateurs de chiffres en C ++ 14 et C ++ 17. Supposons que vous ayez la définition de macro suivante dans l'un des fichiers d'en-tête C ++ 11 purs:
Considérez deux unités de traduction qui incluent le fichier d'en-tête, mais ciblent respectivement C ++ 11 et C ++ 14. Lorsque vous ciblez C ++ 11, la virgule entre guillemets n'est pas considérée comme un séparateur de paramètres; il n'y a qu'un seul paramètre. Par conséquent, le code serait équivalent à:
En revanche, lorsque vous ciblez C ++ 14, les guillemets simples sont interprétés comme des séparateurs de chiffres. Par conséquent, le code serait équivalent à:
Le point ici est que l'utilisation de guillemets simples dans l'un des fichiers d'en-tête purs en C ++ 11 peut entraîner des bogues surprenants dans les unités de traduction qui ciblent C ++ 14/17. Par conséquent, même si un fichier d'en-tête est écrit en C ++ 11, il doit être écrit avec soin pour s'assurer qu'il est compatible avec les versions ultérieures de la norme. La
__cplusplus
directive peut être utile ici.Les trois autres clauses de la norme comprennent:
C.3.2 Article 3: concepts de base
C.3.3 Article 7: déclarations
C.3.4 Article 27: bibliothèque d'entrées / sorties
Les incompatibilités potentielles entre C ++ 14 et C ++ 17 sont discutées en C.4. Étant donné que tous les fichiers d'en-tête non standard sont écrits en C ++ 11 (comme spécifié dans la question), ces problèmes ne se produiront pas, je ne les mentionnerai donc pas ici.
Je vais maintenant discuter de la compatibilité au niveau de l'éditeur de liens. En général, les raisons potentielles d'incompatibilités sont les suivantes:
main
point d'entrée.Si le format du fichier objet résultant dépend du standard C ++ cible, l'éditeur de liens doit être en mesure de lier les différents fichiers objets. Dans GCC, LLVM et VC ++, ce n'est heureusement pas le cas. Autrement dit, le format des fichiers d'objets est le même quel que soit le standard cible, bien qu'il dépende fortement du compilateur lui-même. En fait, aucun des éditeurs de liens de GCC, LLVM et VC ++ n'a besoin de connaissances sur le standard C ++ cible. Cela signifie également que nous pouvons lier des fichiers objets déjà compilés (reliant statiquement le runtime).
Si la routine de démarrage du programme (la fonction qui appelle
main
) est différente pour différentes normes C ++ et que les différentes routines ne sont pas compatibles entre elles, il ne serait pas possible de lier les fichiers objets. Dans GCC, LLVM et VC ++, ce n'est heureusement pas le cas. De plus, la signature de lamain
fonction (et les restrictions qui s'y appliquent, voir Section 3.6 de la norme) est la même dans toutes les normes C ++, donc peu importe dans quelle unité de traduction elle existe.En général, WPO peut ne pas fonctionner correctement avec des fichiers objets compilés à l'aide de différentes normes C ++. Cela dépend exactement des étapes du compilateur qui nécessitent une connaissance de la norme cible et des étapes non et de l'impact que cela a sur les optimisations inter-procédurales qui traversent les fichiers objets. Heureusement, GCC, LLVM et VC ++ sont bien conçus et n'ont pas ce problème (pas que je sache).
Par conséquent, GCC, LLVM et VC ++ ont été conçus pour permettre la compatibilité binaire entre les différentes versions de la norme C ++. Ce n'est cependant pas vraiment une exigence de la norme elle-même.
À propos, bien que le compilateur VC ++ propose le commutateur std , qui vous permet de cibler une version particulière de la norme C ++, il ne prend pas en charge le ciblage C ++ 11. La version minimale qui peut être spécifiée est C ++ 14, qui est la valeur par défaut à partir de Visual C ++ 2013 Update 3. Vous pouvez utiliser une version plus ancienne de VC ++ pour cibler C ++ 11, mais vous devrez alors utiliser différents compilateurs VC ++ pour compiler différentes unités de traduction qui ciblent différentes versions de la norme C ++, ce qui à tout le moins casserait WPO.
CAVEAT: Ma réponse n'est peut-être pas complète ou très précise.
la source
Les nouvelles normes C ++ se divisent en deux parties: les fonctionnalités du langage et les composants de bibliothèque standard.
Comme vous l'entendez par nouveau standard , les changements dans la langue elle-même (par exemple à distance pour) il n'y a presque pas de problème (parfois des conflits existent dans les en-têtes de bibliothèque tiers avec des fonctionnalités de langage standard plus récentes).
Mais bibliothèque standard ...
Chaque version du compilateur est livrée avec une implémentation de la bibliothèque standard C ++ (libstdc ++ avec gcc, libc ++ avec clang, bibliothèque standard MS C ++ avec VC ++, ...) et exactement une implémentation, peu d'implémentations pour chaque version standard. Dans certains cas, vous pouvez également utiliser une autre implémentation de la bibliothèque standard que le compilateur fourni. Ce dont vous devriez vous soucier, c'est de lier une ancienne implémentation de bibliothèque standard à une plus récente.
Le conflit qui pourrait survenir entre les bibliothèques tierces et votre code est la bibliothèque standard (et d'autres bibliothèques) qui est liée à ces bibliothèques tierces.
la source