web-dev-qa-db-fra.com

C ++ 11 a-t-il répondu aux problèmes de passage d'objets std lib entre les limites de bibliothèques dynamiques / partagées? (c.-à-dll et ainsi de suite)?

L'une de mes principales plaintes à propos de C++ est à quel point il est difficile dans la pratique de passer des objets de bibliothèque std en dehors des limites de bibliothèque dynamique (c'est-à-dire dll/so).

La bibliothèque std est souvent uniquement en-tête. Ce qui est génial pour faire des optimisations impressionnantes. Cependant, pour les DLL, ils sont souvent construits avec des paramètres de compilateur différents qui peuvent avoir un impact sur la structure/code interne des conteneurs d'une bibliothèque std. Par exemple, dans MSVC, une DLL peut être construite avec le débogage de l'itérateur activé tandis qu'une autre est désactivée. Ces deux DLL peuvent rencontrer des problèmes lors du passage des conteneurs std. Si j'expose std::string dans mon interface, je ne peux pas garantir le code utilisé par le client pour std::string est une correspondance exacte de std::string.

Cela entraîne des problèmes difficiles à déboguer, des maux de tête, etc. Vous contrôlez de manière rigide les paramètres du compilateur dans votre organisation pour éviter ces problèmes ou vous utilisez une interface C plus simple qui n'aura pas ces problèmes. Ou spécifiez à vos clients les paramètres de compilation attendus qu'ils doivent utiliser (ce qui est nul si une autre bibliothèque spécifie d'autres paramètres de compilation).

Ma question est de savoir si C++ 11 a essayé de faire quoi que ce soit pour résoudre ces problèmes?

34
Doug T.

Vous avez raison de dire que tout élément STL - en fait, tout élément d'une bibliothèque tierce qui est basée sur des modèles - est préférable d'éviter dans n'importe quelle API C++ publique. Vous voulez également suivre la longue liste de règles à http://www.ros.org/reps/rep-0009.html#definition pour empêcher la rupture ABI qui rend la programmation des API C++ publiques une corvée.

Et la réponse concernant C++ 11 est non, cette norme n'y touche pas. Plus intéressant est pourquoi pas? La réponse est parce que C++ 17 le touche beaucoup, et pour que les modules C++ soient implémentés, nous avons besoin de modèles exportés pour fonctionner, et pour cela nous avons besoin d'un compilateur de type LLVM tel que clang qui peut vider l'intégralité AST sur le disque, puis effectuez des recherches dépendantes de l'appelant pour gérer les nombreux cas de violation ODR dans tout grand projet C++ - qui, en passant, comprend de nombreux codes GCC et ELF.

Enfin, je vois beaucoup de commentaires haineux et pro-GCC sur MSVC. Ceux-ci sont très mal informés - GCC sur ELF est fondamentalement et irrémédiablement incapable de produire du code C++ valide et correct. Les raisons en sont nombreuses et légion, mais je citerai rapidement un exemple de cas: GCC sur ELF ne peut pas produire en toute sécurité Python extensions écrites en utilisant Boost.Python où plus d'une extension basée sur Boost. Python est chargé dans Python. En effet, ELF avec sa table de symboles C globale est tout simplement incapable de prévenir les violations ODR provoquant des erreurs de segmentation, tandis que PE et MachO et en effet la spécification proposée pour les modules C++ utilisent tous des tables de symboles par module - ce qui signifie également par ailleurs temps d'initialisation des processus beaucoup plus rapides. Et il y a beaucoup plus de problèmes: voir un StackOverflow auquel j'ai répondu récemment à https://stackoverflow.com/questions/14268736/symbol-visibility-exceptions-runtime-error/14364055#14364055 par exemple, lorsque les levées d'exceptions C++ sont irrémédiablement rompues sur ELF.

Dernier point: en ce qui concerne l'interopérabilité de différentes STL, c'est une grande difficulté pour de nombreux utilisateurs de grandes entreprises qui essaient de mélanger des bibliothèques tierces qui sont étroitement intégrées à certaines implémentations de STL. La seule solution est un nouveau mécanisme pour C++ pour gérer l'interopérabilité STL, et tant qu'ils y sont, vous pouvez aussi corriger l'interopérabilité du compilateur afin que vous puissiez (par exemple) mélanger MSVC, GCC et les fichiers d'objets compilés par clang et tout fonctionne simplement . Je regarderais l'effort C++ 17 et verrais ce qui se passerait là-bas dans les prochaines années - je serais surpris si rien ne se passait.

21
Niall Douglas

La spécification n'a jamais eu ce problème. C'est parce qu'il a un concept appelé "règle d'une définition", qui exige que chaque symbole ait exactement une définition dans le processus en cours.

Les DLL Windows violent cette exigence. Voilà pourquoi il y a tous ces problèmes. C'est donc à Microsoft de le réparer, pas au comité de normalisation C++. Unix n'a jamais eu ce problème, car les bibliothèques partagées y fonctionnent différemment et se conforment par défaut à une règle de définition (vous pouvez explicitement la casser, mais vous ne le faites évidemment que si vous savez que vous pouvez vous le permettre et que vous devez éliminer les quelques cycles supplémentaires).

Les DLL Windows violent une règle de définition car:

  • Ils codent en dur à partir de quelle bibliothèque dynamique un symbole sera utilisé pendant le temps de liaison statique et résolvent statiquement les symboles dans la bibliothèque qui les définit. Donc, si le même symbole faible est généré dans plusieurs bibliothèques partagées et ces bibliothèques que celui utilisé dans un seul processus, l'éditeur de liens dynamique n'a aucune chance de fusionner ces symboles. Habituellement, ces symboles sont des membres statiques ou des empêchements de classe d'instances de modèle et cela provoque des problèmes lors du passage d'instances entre du code dans différentes DLL.
  • Ils codent en dur si le symbole sera importé de la bibliothèque dynamique déjà pendant la compilation. Ainsi, le code lié statiquement à une bibliothèque est incompatible avec le code lié dynamiquement à la même bibliothèque.

Unix utilisant les exportations au format ELF importe implicitement tous les symboles exportés pour éviter le premier problème et ne fait pas de distinction entre les symboles résolus statiquement et dynamiquement jusqu'au moment du lien statique pour éviter le second.


L'autre problème concerne les drapeaux du compilateur. Ce problème existe pour tout programme composé de plusieurs unités de compilation, les bibliothèques dynamiques ne doivent pas être impliquées. Cependant, c'est bien pire sous Windows. Sous Unix, peu importe que vous établissiez un lien statique ou dynamique, personne ne lie quand même statiquement le runtime standard (sous Linux, il peut même être illégal) et il n'y a pas de runtime de débogage spécial, donc une build est assez bonne. Mais la façon dont Microsoft a implémenté la liaison statique et dynamique, le débogage et l'exécution de la version et certaines autres options signifie qu'elles ont provoqué une explosion combinatoire des variantes de bibliothèque nécessaires. Encore une fois, un problème de plate-forme plutôt qu'un problème de langage C++.

8
Jan Hudec

Non.

Il y a beaucoup de travail en cours pour remplacer le système d'en-tête, une fonctionnalité qui s'appelle Modules et qui pourrait avoir un impact sur cela, mais certainement pas un gros.

6
Klaim