2008-12-02 14 views
11

Nous avons une bibliothèque C++ que nous fournissons à plusieurs clients différents. Récemment nous avons fait le changement d'utiliser des pointeurs bruts dans l'interface publique pour utiliser boost :: sharedptr à la place. Cela a fourni un énorme avantage, comme vous pouvez le deviner, en ce sens que les clients n'ont plus à s'inquiéter de savoir qui doit supprimer quoi et quand. Quand nous avons fait le changement, je pensais que c'était la bonne chose à faire, mais cela m'a dérangé que nous devions inclure quelque chose d'une bibliothèque tierce dans notre interface publique - en général, vous évitez ce genre de chose si vous le pouvez. J'ai rationalisé le fait que boost était pratiquement une partie du langage C++ maintenant, et notre cas d'utilisation nécessite que le code client et la bibliothèque contiennent des pointeurs sur les objets. Cependant, récemment, un de nos clients nous a demandé si nous pouvions passer à l'utilisation d'une classe pointeur intelligent neutre dans l'interface, car notre bibliothèque les oblige essentiellement à une version particulière du point de boost que je comprends et apprécie certainement. Alors maintenant je me demande quelle pourrait être la meilleure ligne de conduite. J'y ai réfléchi un peu et je me suis posé la question de créer une simple classe de pointeurs intelligents qui contenait simplement un vrai pointeur intelligent. Mais alors les clients ajouteraient probablement immédiatement un de ceux-ci dans leur saveur de boost :: sharedptr, et ensuite nous aurions trois profondeurs partagées - ce qui pourrait être un problème, ou peut-être pas. Quoi qu'il en soit, j'aimerais entendre des opinions de la communauté sur la meilleure façon de résoudre ce problème. Edit: J'ai d'abord dit transfert de propriété, mais j'aurais dû préciser que le code des deux côtés de la frontière API doit contenir un pointeur vers l'objet.Utilisation de boost :: shared_ptr dans l'interface publique d'une bibliothèque

Répondre

13

shared_ptr <>est partie du langage, à la sortie de TR1. Voir: (TR1)

+1

Oui, mais nous ne pouvons pas forcer nos clients à passer à Visual Studio 2008, où TR1 est inclus, IIRC. Certains sont encore sur VS 2005. –

+1

Pourriez-vous inclure le dossier Boost TR1 avec vos en-têtes de bibliothèque? Je devrais regarder la licence pour voir si c'est légal, mais c'est probablement le cas. De plus, boost :: shared_ptr <> étant simplement un template, aucune librairie partagée ou statique n'est nécessaire. –

+2

Notez que TR1 ne fait pas partie du langage en tant que tel, mais qu'il s'agit d'une collection informative (officielle) de bibliothèques qui pourrait faire partie d'une future norme linguistique. Voir ici: http://www.iso.org/iso/standards_development/processes_and_procedures/deliverables/iso_tr_deliverable.htm –

4

Ceci est C++. Vous savez, vous pouvez modéliser la classe d'interface sur l'implémentation du pointeur partagé.

+2

... rendant ainsi impossible la dissimulation de l'implémentation de vos interfaces car elles doivent toutes être des modèles. Pas une bonne idée pour quelqu'un qui essaie de vendre un logiciel propriétaire – Bklyn

+0

@Bklyn: Je n'oserais pas utiliser une bibliothèque C++ tierce sans au moins une source compressée. L'ABI C++ est juste trop fragile. – Joshua

6

Si la sémantique est vraiment transfert de propriété, pourquoi ne pas utiliser auto_ptr car c'est le standard C++? En interne, vous pouvez toujours construire votre shared_ptr à partir de auto_ptr et avoir la propriété partagée si vous en avez besoin.

3

Vous pouvez utiliser l'utilitaire boost copy pour construire une version personnalisée de boost qui avait seulement la classe de pointeur intelligent. Étant donné que la classe de pointeurs intelligents est une bibliothèque en-tête uniquement, cela devrait entraîner quelques en-têtes que vous pourriez inclure dans votre bibliothèque.

18

Une solution possible est d'expédier boost :: shared_ptr avec votre projet. Comme tout est composé d'en-têtes, vos clients n'auront plus à installer manuellement les bibliothèques de boost. Vous pouvez utiliser bcp pour obtenir tous les fichiers requis par une bibliothèque de relance particulière, y compris les bibliothèques elles-mêmes. Je l'ai fait quand je travaillais pour une entreprise à l'époque et avait besoin de boost::shared_ptr et cela a vraiment beaucoup fonctionné.

3

Ceci est une question intéressante que j'ai eu depuis un certain temps. Obligez-vous vos utilisateurs dans la bibliothèque que vous fournissez, ou laissez-les décider de ce qui est le mieux dans leur projet? Comme toujours, la question est ce que vous offrez et ce que vous demandez à l'utilisateur.

Si vous utilisez des pointeurs bruts, vous autorisez toutes sortes de possibilités. Le code utilisateur peut utiliser un pointeur brut, le stocker dans std :: auto_ptr, shared_ptr (boost ou TR1), ou leur version homebrewed d'un pointeur intelligent. Mais cela peut aussi causer des problèmes à l'utilisateur s'il oublie de libérer la mémoire, et il lui faut un peu plus de code s'il veut simplement créer temporairement un appel de méthode (si vous fournissez des pointeurs bruts, ils devront stocker le pointeur dans une variable de pointeur non temporaire [éventuellement intelligent]).Maintenant, si vous utilisez un pointeur intelligent, vous forcez votre solution dans l'utilisateur. S'ils envisagent d'utiliser leur propre version d'un pointeur intelligent (disons que vous utilisez boost :: shared_ptr et qu'ils veulent std :: tr1 :: shared_ptr), ils ne sont plus autorisés à l'utiliser s'ils fonctionnent avec votre interface. Quel que soit le pointeur intelligent que vous choisissez (en plus de std :: auto_ptr qui est spécial), vous ne forcez pas seulement une solution, mais aussi les problèmes qu'il a.

Si votre utilisateur dispose d'une application multithread et que votre solution n'est pas sécurisée pour les threads, l'utilisateur est lié à une solution non sécurisée. Si, par contre, le pointeur intelligent est thread-safe mais entraîne des coûts de verrouillage, ces coûts sont répercutés sur vos utilisateurs même s'ils travaillent dans une application multithread. Si vous compilez votre bibliothèque (pas un en-tête uniquement lib), vous forcez non seulement un type de pointeur intelligent, mais aussi une version particulière de celui-ci, car toute modification de la bibliothèque de pointeurs intelligents rompt la compatibilité de votre code. En outre, boost :: shared_ptr (boost 1.33+) est thread safe dans la plupart des cas, et utilise une implémentation sans verrou sur de nombreuses plates-formes. De toute façon cela devrait vous donner une idée des choses que vous devriez considérer. Enfin, vous devez considérer que vous n'êtes pas seulement obligé d'utiliser votre type de pointeur intelligent, mais aussi la même version. Si vous compilez votre lib sur une version particulière de boost, l'utilisateur est lié à cette implémentation particulière. O

6

Tout d'abord, si vous distribuez votre bibliothèque en tant que code source plutôt qu'en tant que bibliothèque compilée, vous pouvez ignorer cette réponse. Il existe également des problèmes spécifiques aux fenêtres qui peuvent ne pas être pertinents pour d'autres plates-formes.

Je pense personnellement que vous devriez éviter d'avoir trop de C++ funky dans l'interface publique de votre bibliothèque car cela peut causer beaucoup de problèmes au client. Je ne sais pas si cela est applicable à votre exemple particulier, mais j'ai personnellement rencontré des problèmes lorsque les symboles de la bibliothèque stl que j'ai utilisés étaient en conflit avec ceux de la bibliothèque tierce lors de la mise à niveau vers une nouvelle version . Cela signifiait que nous avions des accidents dans des endroits étranges et que je devais faire beaucoup de trucs pour éviter le problème. À la fin, je suis resté avec l'ancienne version de la bibliothèque à cause de cela. Un autre problème que vous pouvez rencontrer est que différents compilateurs C++ peuvent modifier les mêmes symboles différemment, ce qui signifie que vous devez potentiellement fournir une bibliothèque séparée pour chaque compilateur que vous voulez prendre en charge même s'ils utilisent la même version de Boost. Consultez le livre "Imperfect C++" pour une discussion à ce sujet. Dans le monde actuel des différents compilateurs et environnements C++, la triste vérité est que vous devriez éviter d'avoir autre chose que C dans votre interface et assurez-vous de lier dynamiquement votre bibliothèque (pour éviter les conflits lorsque vous liez vos clients à votre bibliothèque) , bibliothèque d'exécution de Windows peut être une vraie douleur ici). Vous pouvez toujours utiliser boost et autant de C++ fantaisie à l'intérieur de votre bibliothèque que vous le souhaitez car tous vos symboles seront isolés de l'environnement de vos clients dans la DLL.

Si vous voulez vraiment avoir des pointeurs intelligents et d'autres fonctionnalités C++ dans l'interface de votre bibliothèque, créez un calque pratique pour lequel vous distribuez le code source. Cela permettra de s'assurer qu'il est toujours compilé dans l'environnement des clients. Cette interface appelle ensuite vos fonctions C exposées de manière intelligente. Je ne pense pas que ce soit une bonne idée d'utiliser boost dans cette couche car cela forcera vos clients à l'adopter même s'ils ne veulent pas, mais il est facile de le remplacer ou de trouver une autre solution puisque cette couche est distribuée code.

Une autre fonctionnalité intéressante est qu'il est généralement plus facile d'appeler des fonctions C dans une DLL que des fonctions C++ avec mangling de nom étrange si vous souhaitez exposer votre bibliothèque à d'autres langages que C/C++. Cette approche rend votre vie plus compliquée à bien des égards, mais c'est une façon de réduire le risque que les gens évitent votre bibliothèque car il était impossible de lier avec succès leur propre code.

1

L'introduction de boost :: shared_ptr force votre client à utiliser boost. pour certaines personnes, c'est un problème mineur.

il force également vos clients à utiliser le même compilateur que celui utilisé par votre lib, si votre lib est distribuée en tant que binaire compilé. ou, si votre bibliothèque est distribuée dans le code source, les clients doivent s'en tenir à leur propre choix de compilateur utilisé pour compiler votre lib. Ce n'est pas un problème mineur pour un projet de taille considérable.

1

Utilisez auto_ptr ou respectez une interface C. Forcer les librairies C++ dans votre interface est toujours moche, tue toute chance d'être multi-plateforme, et provoque un cauchemar de maintenance pour les clients avec différentes configurations "en aval".

Dès que C++ 0x est assez courant pour vos clients, passez à std::shared_ptr.