Il y a beaucoup de problèmes de portabilité avec C ++, ce qui est uniquement dû au manque de standardisation au niveau binaire.
Je ne pense pas que ce soit aussi simple que cela. Les réponses fournies fournissent déjà une excellente justification du manque de concentration sur la normalisation, mais C ++ peut être trop riche en langage pour être bien adapté pour concurrencer véritablement C en tant que norme ABI.
Nous pouvons entrer dans le changement de nom résultant de la surcharge de fonctions, des incompatibilités de vtable, des incompatibilités avec des exceptions qui dépassent les limites des modules, etc.
Mais une norme ABI ne consiste pas seulement à rendre les dylibs C ++ produits dans un compilateur capables d'être utilisés par un autre binaire construit par un compilateur différent. ABI est utilisé dans plusieurs langues . Ce serait bien s'ils pouvaient au moins couvrir la première partie, mais je ne vois aucun moyen que C ++ soit vraiment en concurrence avec C au niveau ABI universel si crucial pour créer les dylibs les plus compatibles.
Imaginez une simple paire de fonctions exportées comme ceci:
void f(Foo foo);
void f(Bar bar, int val);
... et imaginez Foo
et Bar
étaient des classes avec des constructeurs paramétrés, des constructeurs de copie, des constructeurs de déplacement et des destructeurs non triviaux.
Ensuite, prenez le scénario d'un Python / Lua / C # / Java / Haskell / etc. développeur essayant d'importer ce module et de l'utiliser dans leur langue.
Tout d'abord, nous aurions besoin d'une norme de gestion de noms pour exporter des symboles en utilisant la surcharge de fonctions. C'est une partie plus facile. Pourtant, ce ne devrait pas vraiment être un nom de "mutilation". Étant donné que les utilisateurs de dylib doivent rechercher les symboles par nom, les surcharges ici devraient conduire à des noms qui ne ressemblent pas à un gâchis complet. Peut-être que les noms des symboles pourraient être similaires "f_Foo"
"f_Bar_int"
ou quelque chose de ce genre. Nous devons être sûrs qu'ils ne peuvent pas entrer en conflit avec un nom réellement défini par le développeur, réservant peut-être certains symboles / caractères / conventions à l'utilisation d'ABI.
Mais maintenant, un scénario plus difficile. Comment le développeur Python, par exemple, invoque-t-il des constructeurs de déplacement, des constructeurs de copie et des destructeurs? Nous pourrions peut-être les exporter dans le cadre du dylib. Mais que faire si Foo
et Bar
sont exportés dans différents modules? Faut-il dupliquer ou non les symboles et implémentations associés dans ce dylib? Je suggérerais que nous le fassions, car cela pourrait devenir très ennuyeux très rapidement sinon de commencer à devoir être emmêlé dans plusieurs interfaces dylib juste pour créer un objet ici, le passer ici, le copier ici, le détruire ici. Alors que la même préoccupation de base pourrait quelque peu s'appliquer en C (juste plus manuellement / explicitement), C a tendance à éviter cela simplement par la manière dont les gens programment avec.
Ce n'est qu'un petit échantillon de la maladresse. Que se passe-t-il lorsque l'une des f
fonctions ci-dessus lance une BazException
(également une classe C ++ avec des constructeurs et des destructeurs et dérivant std :: exception) en JavaScript?
Au mieux, je pense que nous ne pouvons qu'espérer normaliser un ABI qui fonctionne d'un binaire produit par un compilateur C ++ à un autre binaire produit par un autre. Ce serait formidable, bien sûr, mais je voulais juste le souligner. Habituellement, le souci de distribuer une bibliothèque généralisée qui fonctionne avec des compilateurs croisés accompagne généralement le désir de la rendre vraiment généralisée et compatible.
Solution suggérée
Ma solution suggérée après avoir eu du mal à trouver des moyens d'utiliser des interfaces C ++ pour les API / ABI pendant des années avec des interfaces de style COM est de devenir un développeur "C / C ++" (jeu de mots).
Utilisez C pour créer ces ABI universels, avec C ++ pour l'implémentation. Nous pouvons toujours faire des choses comme des fonctions d'exportation qui renvoient des pointeurs vers des classes C ++ opaques avec des fonctions explicites pour créer et détruire de tels objets sur le tas. Essayez de tomber amoureux de cette esthétique C du point de vue ABI même si nous utilisons totalement C ++ pour la mise en œuvre. Les interfaces abstraites peuvent être modélisées à l'aide de tableaux de pointeurs de fonction. Il est fastidieux de regrouper ces éléments dans une API C, mais les avantages et la compatibilité de la distribution qui l'accompagne auront tendance à en faire très la peine.
Ensuite, si nous n'aimons pas autant utiliser cette interface directement (nous ne devrions probablement pas au moins pour des raisons RAII), nous pouvons envelopper tout ce que nous voulons dans une bibliothèque C ++ liée statiquement que nous livrons avec le SDK. Les clients C ++ peuvent l'utiliser.
Les clients Python ne voudront pas utiliser directement une interface C ou C ++ car il n'y a aucun moyen de les rendre pythoniques. Ils voudront l'intégrer dans leurs propres interfaces pythoniques, donc c'est en fait une bonne chose que nous exportions juste un strict minimum C API / ABI pour rendre cela aussi simple que possible.
Je pense que beaucoup de l'industrie C ++ gagnerait à le faire plutôt qu'à essayer de livrer obstinément des interfaces de style COM et ainsi de suite. Cela faciliterait également toute notre vie en tant qu'utilisateurs de ces dylibs pour ne pas avoir à se soucier des ABI maladroits. C le rend simple, et sa simplicité dans une perspective ABI nous permet de créer des API / ABI qui fonctionnent naturellement et avec minimalisme pour toutes sortes de FFI.