11
votes

Quelle est la meilleure pratique pour l'API publique C ++?

Quelle est la meilleure pratique pour l'API publique C ++?

Je travaille sur un projet C ++ comportant plusieurs espaces de noms, chacun avec plusieurs objets. Certains objets ont les mêmes noms, mais sont dans des espaces de noms différents. Actuellement, chaque objet a son propre fichier .CPP et fichier .h. Je ne suis pas sûr de savoir comment le mot ... serait-il approprié de créer un deuxième fichier .h pour exposer uniquement l'API publique? Devrait-il être un fichier .h fichier par espace de noms ou par objet ou une autre portée? Qu'est-ce qui pourrait être une meilleure pratique pour créer des API publiques pour les bibliothèques C ++?

Merci pour toute aide, Chenz


0 commentaires

6 Réponses :


1
votes

Je dirais que c'est mieux décidé par vous et le type de "bibliothèque" c'est.

Votre API fournit-il une "action"? ou ne gère qu'un seul "type de données" abstrait? Des exemples pour cela seraient de zlib et de libpng. Les deux n'ont qu'un en-tête qui donne tout ce qui est nécessaire pour effectuer ce que sont les bibliothèques.

Si votre bibliothèque est une collection de classes non liées (ou même liées) qui font, ou pas, le même objectif, fournissent ensuite chaque sous-ensemble avec sa propre tête. Un exemple majeur pour cela sera boost.


0 commentaires

2
votes

Il est parfois pratique d'avoir une seule classe dans chaque fichier .CPP et .h paire de fichiers et d'avoir la hiérarchie des espaces de noms comme la hiérarchie de la répertoire.
Par exemple, si vous avez cette classe: xxx

alors il sera dans deux fichiers: xxx

Une autre mise en page possible pourrait être la suivante: xxx


2 commentaires

Mais lorsque vous mettez tout ensemble dans une seule bibliothèque .so, puis-je distribuer une structure de répertoires en profondeur? Chenz


@Crazy Chenz, oui, c'est une possibilité. C'est ainsi que cela se fait dans le noyau Linux par exemple et aussi à booster dans une certaine mesure.



0
votes

Grande réponse Liranuna.

fournissez-vous une API à une application ou à une bibliothèque?

Une API d'application ne fournira généralement que des méthodes qui interrogent l'état d'une application ou une tentative de modification de cet état. Dans ce cas, vous auriez généralement des déclarations d'interface distinctes dans un fichier d'en-tête séparé. Vos objets implémentaient ensuite cette interface pour gérer les demandes d'API.

Une bibliothèque exposera généralement des objets pouvant être réutilisés. Dans ce cas, de manière générale, votre API est simplement les méthodes publiques du fichier d'en-tête.


7 commentaires

J'ai une bibliothèque principale qui devrait être réutilisable, puis quelques espaces de noms contenant tous les objets et aides aux applications qui utilisent le noyau. Le noyau est ce que je veux "exposer" une API publique pour.


Il semble suffit d'avoir étrange d'avoir .h fichiers qui montrent les définitions des méthodes privées / Vars dans ma distribution.


Ok donc ma ligne directrice il y aurait - réduire autant que possible la portée possible. Si j'inclus 'foo.h', ne me faites pas hériter d'un tas de fonctions et d'objets que je ne me soucie pas. Considérons stl - i #include pas . Tant que votre interface est complète et bien définie, il est préférable que de nombreuses inclues dans le code de la clientèle (IMHO).


Le compilateur n'élimine pas le gonflement? Dans les bibliothèques telles que GTK et XLIB, vous incluez GTK / GTK.H ou X11 / XLIB.H et c'est cela. Sont-ils sacrifiants la conservation pour le code de nettoyage?


Je suppose que dans ces cas, je pourrais être plus précis, mais ils ont ajouté un fichier d'en-tête de convienne qui inclut BLOAT mais me permet de ne pas avoir à savoir tout le reste.


heh ... un #include pourrait être agréable d'avoir (surtout si le compilateur se débarrasse du "BLOQUE")


Bien sûr, comme je l'ai dit - c'est ce que je préférerais. Cela me semble que vous êtes tout à fait capable de prendre la bonne décision pour votre projet. Mon dernier conseil - Demandez aux clients (développeurs) ce qu'ils préfèrent. Préparez-vous à entendre Tous les deux ! :RÉ



2
votes

g'day,

Une suggestion est de jeter un coup d'œil à l'idiome C ++ de la poignée, parfois appelé Cheshire Cat. Voici Le papier original de James Coplien contenant l'idiome.

Il s'agit d'une méthode bien connue pour découpler des API publiques des implémentations.

htth


2 commentaires

Un autre nom de cet idiome (que je pense est devenu plus populaire, mais je me tromperais peut-être) est le "idiome pimpl" - le pointeur à la mise en œuvre.


@Michael, tu as raison. Ce nom a également été ajouté mais assez récemment IIRC. Poignée / corps était le nom original Cope l'a donné dans son livre de 1991 "Styles de programmation avancée C ++ et idiomes" << a href = "https://rads.stackoverflow.com/amzn/click/com/0201548550" rel = "nofollow NOREFERRER "REL =" NOFOOLS NOREFERRER "> Amazon.com/0201548550 >



1
votes

Voici ce que je suis habitué à faire:

"Certains objets ont les mêmes noms, mais sont dans différents espaces de noms"

C'est pourquoi les espaces de noms existent.

"serait-il approprié de créer un deuxième fichier .h pour exposer uniquement l'API publique?"

Vous devez toujours exposer uniquement l'API publique. Mais que signifie exposer une API publique? Si ce serait seulement aux en-têtes, puisque l'API public s'appuie sur une API privée, l'API privée serait incluse de toute façon par l'API publique. Pour exposer une API publique Marquer des fonctions / classes publiques avec une macro (qui, dans le cas de Windows, exporte des fonctions publiques sur la table des symboles; et il sera probablement bientôt adopté par les systèmes UNIX). Vous devriez donc définir une macro comme MyLib_API ou MyLib_DeclSpec, simplement vérifier certaines bibliothèques existantes et la documentation Mme DeclSpec. Il suffit, généralement des API non publiques seront conservés dans des sous-répertoires afin qu'il n'atteigne pas l'utilisateur de la bibliothèque.

"devrait être un fichier .h fichier par espace de noms ou par objet ou une autre portée?"

Je préfère le style Java, un Public classe par en-tête. J'ai constaté que les libs écrits de cette manière sont beaucoup plus propres et lisibles que ceux qui mélangent des noms de fichiers et de structures. Mais il y a des cas lorsque je freine cette règle, surtout quand il s'agit de modèles. Dans de tels cas, je donne un message # pour ne pas inclure d'en-tête directement et d'expliquer soigneusement les commentaires Qu'est-ce qui se passe.


3 commentaires

"Mais que signifie exposer une API publique?" Je pensais dans le sens de la reproduction de tous les en-têtes de distribution sans leurs méthodes privées et Vars.


Je veux dire .... Sans leurs prototypes de méthode privée et leurs déclarations variables privées.


Cela n'a pas de sens imo. En fait, cela rendrait votre bibliothèque seulement plus difficile à comprendre et à entretenir. Si quelqu'un doit prolonger votre classe, il aimerait probablement voir une structure de classe entière et non seulement des membres publics et protégés. La définition de la classe doit être définitivement conservée dans un en-tête.



0
votes

Je suis d'accord avec ce que Doc a dit - une classe par fichier. 99,9% du temps!

En outre, considérons les noms de fichiers à utiliser. C'est généralement une mauvaise idée d'avoir plusieurs en-têtes du même nom dans différents répertoires, même si les classes qu'ils contiennent peuvent bien être dans différents espaces de noms.

Surtout s'il s'agit d'une API publique, vous ne pouvez probablement pas contrôler ce que les chemins incluent sont définis par l'utilisateur de votre bibliothèque, donc une construction peut trouver le mauvais. Modification de l'ordre des chemins d'inclusion serait définitivement pas une solution que je recommanderais!

Nous utilisons une convention de dénomination de noms espaces-class.h pour identifier explicitement les classes dans les fichiers.


4 commentaires

Puis-je vous demander ce que vous faites avec des espaces de noms longs? Disons que vous avez une classe myLib :: graphisme :: formats :: jpegloader. Nom en-tête myLib_Graphics_formats-jpegloader.h? Il est quelque peu frustrant d'utiliser de tels en-têtes. OMI La meilleure façon d'éviter les inclusions accidentelles est de forcer votre nom de la bibliothèque à faire partie du chemin d'inclusion, dans notre exemple inclusion ressemblerait à #include . Jusqu'à ce que vous ayez "myLib" dans <> tout ira bien.


Heureusement, nos espaces de noms ne sont pas imbriqués aussi profondément; Un seul espace de noms, rarement 2, donc ce n'est pas trop mauvais. De quelle manière est-il frustrant - et quelle est la différence entre #include "myLib-graphiques-formats-jpegloader.h" ou #include "myLib / graphiques / formats / jpegloader.h" < / code>? Vous devez toujours taper le 'chemin' complet en quelque sorte?


Vous pouvez avoir #include Si vous gardez la structure de répertoire en ce qui concerne les espaces de noms, et sur mon côté, il serait . Si vous gardez tout dans un répertoire, alors OK.


@Doc: OOH, YUK! :-) Nous utilisons un arborescence plat et mettons la "structure" dans les noms de fichiers. Cela empêche l'ambiguïté lorsque vous avez (dans votre exemple) 2 fichiers jpegloader.h. Je peux voir si vous forcez les utilisateurs de votre API à définir leurs chemins inclus pour indiquer où myLib est alors que vous n'avez pas d'ambiguïté. Mais vous ne pouvez pas toujours garantir que les utilisateurs feront la bonne chose (ou vous pouvez garantir qu'ils seront faire la mauvaise chose!).