Comment concevoir une API C++ pour compatible binaire extensibilité
Je suis en train de concevoir une API pour une bibliothèque C++ qui sera distribué dans une dll /objet partagé. La bibliothèque contient polymorhic classes avec des fonctions virtuelles. Je crains que si j'expose ces fonctions virtuelles sur l'API DLL, j'ai coupé moi-même de la possibilité d'étendre les mêmes classes avec plus de fonctions virtuelles sans casser la compatibilité binaire avec les applications développées pour la version précédente de la bibliothèque.
Une option serait d'utiliser le PImpl idiome pour masquer toutes les classes ayant des fonctions virtuelles, mais qui semble aussi avoir ses limites: de cette façon, les applications de perdre la possibilité de dériver les classes de la bibliothèque et en remplaçant les méthodes virtuelles.
Comment voulez-vous concevoir une classe de l'API qui peut être sous-classé dans une application, sans perdre la possibilité d'étendre l'API avec (non pas abstraite) des méthodes virtuelles dans une nouvelle version de la dll, tout en restant en arrière compatible binaire?
Mise à jour: les plates-formes cibles pour la bibliothèque sont windows/msvc et linux/gcc.
Vous devez vous connecter pour publier un commentaire.
Il y a plusieurs mois, j'ai écrit un article intitulé "Compatibilité Binaire des Bibliothèques Partagées Implémenté en C++ sur des Systèmes GNU/Linux" [pdf]. Alors que les concepts sont similaires sur le système Windows, je suis sûr qu'ils ne sont pas exactement les mêmes. Mais après avoir lu l'article, vous pouvez obtenir une idée de ce qui se passe au C++ niveau binaire qui n'a rien à voir avec la compatibilité.
Par la voie, GCC application binary interface est résumée dans un document standard projet "Itanium ABI", de sorte que vous aurez un officiel de sol pour une norme de codage que vous choisissez.
Juste pour un exemple rapide: dans GCC, vous pouvez étendre une classe avec plus de fonctions virtuelles, si aucune autre classe hérite. Lire l'article pour mieux définir des règles.
Mais de toute façon, les règles sont parfois trop complexes à comprendre. De sorte que vous pourriez être intéressé par un outil qui permet de vérifier la compatibilité de deux versions: abi-conformité-checker pour Linux.
Il y a un article intéressant sur le KDE de la base de connaissances qui décrit le faire et ne pas faire lorsque l'on vise à la compatibilité binaire lors de l'écriture d'une bibliothèque: Politiques/Binaire Des Problèmes De Compatibilité Avec Le C++
C++ binaire compat est généralement difficile, même sans héritage. Regardez GCC par exemple. Dans les 10 dernières années, je ne suis pas sûr de savoir comment beaucoup de rupture de LCA des changements qu'ils ont eu. Puis MSVC a un ensemble différent de conventions, de sorte que la liaison avec GCC et vice-versa ne peut pas être fait... Si vous comparez cela à la C monde, compilateur inter-op semble un peu mieux là.
Si vous êtes sur Windows, vous devriez regarder COM. Comme vous introduire de nouvelles fonctionnalités, vous pouvez ajouter des interfaces. Ensuite, les appelants peuvent
QueryInterface()
pour la nouvelle exposer de nouvelles fonctionnalités, et même si vous avez à changer les choses, vous pouvez soit laisser la vieille mise en œuvre ou vous pouvez écrire des cales pour les anciennes interfaces.Je pense que vous ne comprenez pas le problème de sous-classement.
Ici est votre Pimpl:
Voir ? Pas de problème avec surchargeant les méthodes virtuelles de
Base
, vous avez juste besoin de faire des redeclare euxvirtual
dansDerived
ainsi que ceux découlant de Dérivés savent qu'ils peuvent réécrire trop (seulement si vous le souhaitez, qui en passant est un excellent moyen de fournir unfinal
pour ceux qui n'en disposent pas), et vous pouvez toujours redéfinir pour vous-même dansImpl
qui peut même appeler leBase
version.Il n'y a pas de problème avec
Pimpl
là.Sur l'autre main, vous perdez le polymorphisme, ce qui peut être gênant. C'est à vous de décider si vous voulez polymorphisme ou tout simplement de la composition.
virtual
les méthodes de l'interface.Si vous exposez le PImpl classe dans un fichier d'en-tête, alors vous pouvez hériter d'elle. Vous pouvez toujours préserver la portabilité depuis les classes externes contient un pointeur vers le PImpl objet. Bien sûr, si le code client de la bibliothèque, ce n'est pas très sage, il pourrait un mauvais usage de cet exposé PImpl objet, et de la ruine de l'binaire de compatibilité descendante. Vous pouvez ajouter quelques notes pour avertir l'utilisateur dans le PImpl du fichier d'en-tête.