IdentifiantMot de passe
Loading...
Mot de passe oublié ?Je m'inscris ! (gratuit)
Navigation

Inscrivez-vous gratuitement
pour pouvoir participer, suivre les réponses en temps réel, voter pour les messages, poser vos propres questions et recevoir la newsletter

C++ Discussion :

Organisation des fichiers


Sujet :

C++

  1. #1
    Membre expérimenté
    Homme Profil pro
    Chercheur
    Inscrit en
    Mars 2010
    Messages
    1 218
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Localisation : France, Paris (Île de France)

    Informations professionnelles :
    Activité : Chercheur

    Informations forums :
    Inscription : Mars 2010
    Messages : 1 218
    Points : 1 685
    Points
    1 685
    Par défaut Organisation des fichiers
    Bonjour,

    je me pose des questions sur la manière optimale d'organiser mes fichiers.

    Je suis depuis un moment le schéma suivant pour une classe Foo :
    - Foo_fwd.hpp contient la déclaration anticipée de Foo;
    - Foo.hpp contient la déclaration de la classe Foo;
    - Foo_inline.hpp contient les fonctions membres inline;
    - Foo_template contient les fonctions membres template;
    - Foo.cpp contient l'impélmentation qui n'est ni template ni inline;
    - Foo_friend.hpp contient les déclarations des fonctions amies.

    J'ai notamment un problème avec les fonctions amies.
    Je ne sais jamais vraiment où les ranger quand elles sont amies de plusieurs classes.

    Comment procédez-vous?

  2. #2
    Rédacteur/Modérateur


    Homme Profil pro
    Network game programmer
    Inscrit en
    Juin 2010
    Messages
    7 115
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Âge : 36
    Localisation : Canada

    Informations professionnelles :
    Activité : Network game programmer

    Informations forums :
    Inscription : Juin 2010
    Messages : 7 115
    Points : 32 967
    Points
    32 967
    Billets dans le blog
    4
    Par défaut
    Salut,

    t'es adepte de l'over-engeenering et de la prise de tête non ?

    Foo_fwd.hpp contient la déclaration anticipée de Foo;
    Tu veux dire que tu as un fichier pour écrire class Foo; ?

    Après tout, pourquoi faire simple quand on peut faire compliqué !

    Foo.hpp
    Foo.cpp
    Foo.tpl (inl, ...) pour l'implémentation template - quand c'est pas juste à la fin du hpp, ou dans son corps

    Quelqu'un a besoin d'une simple forward declaration ? Il écrit class Foo;. Au passage, ça sera plus simple, moins chiant et plus rapide que #include <Super/Path/To/Foo_fwd.hpp>
    Les fonctions inline ? Ben dans le hpp elles sont très bien.
    Les déclaration d'amitié ? Elles sont dans le corps de la class.
    Une fonction amie de plusieurs classes, c'est peut-être plutôt un problème de conception.
    Pensez à consulter la FAQ ou les cours et tutoriels de la section C++.
    Un peu de programmation réseau ?
    Aucune aide via MP ne sera dispensée. Merci d'utiliser les forums prévus à cet effet.

  3. #3
    Membre expérimenté
    Homme Profil pro
    Chercheur
    Inscrit en
    Mars 2010
    Messages
    1 218
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Localisation : France, Paris (Île de France)

    Informations professionnelles :
    Activité : Chercheur

    Informations forums :
    Inscription : Mars 2010
    Messages : 1 218
    Points : 1 685
    Points
    1 685
    Par défaut
    Bonsoir Bousk,

    merci pour ta réponse.

    Je ne partage pas ton point de vue concernant les déclarations anticipées.
    Avec ton approche, tu ne factorises pas ton code.
    Si tu changes le nom de ta classe, si tu ajoutes un paramètre template, etc, tu devras répercuter ta modification partout.
    Par ailleurs, je ne vois pas en quoi réécrire explicitement tes déclarations anticipées est plus simple ou plus rapide à écrire, surtout si tu manipules des classes plus avancées possédant plusieurs paramètres template.

    Pour le fait de réunir les fonctions inline et template dans un seul et unique fichier d'implémentation foo_impl.hpp, c'est effectivement une question que je me pose.
    Le fait de séparer les fonctions inline me paraît intéressant quand on fait beaucoup d'optimisation.
    On voit plus facilement où il n'y a pas d'expansion de code.
    Mais c'est peut-être inutilement compliqué, j'en conviens.

    Enfin, avoir une fonction amie de plusieurs classes ne me paraît pas du tout être un problème de conception.
    C'est par exemple le cas typique quand tu as une classe Vecteur, une classe Matrice, et une fonction amie de ces deux classes calculant le produit entre une matrice et un vecteur.

  4. #4
    Membre expert
    Profil pro
    Inscrit en
    Mars 2007
    Messages
    1 415
    Détails du profil
    Informations personnelles :
    Localisation : France, Paris (Île de France)

    Informations forums :
    Inscription : Mars 2007
    Messages : 1 415
    Points : 3 156
    Points
    3 156
    Par défaut
    Hello

    Citation Envoyé par Aleph69 Voir le message
    Si tu changes le nom de ta classe, si tu ajoutes un paramètre template, etc, tu devras répercuter ta modification partout.
    Tu te contredis toi même, avec ta méthode, si tu changes le nom de ta classe, il faut aussi renommer tous les fichiers et changer tous les includes correspondants, ce qui revient au même. Et de toute façon, il faudra changer les endroits ou le nom de cette classe est utilisé : ton header n'y change rien, tu n'économises qu'une seule ligne de changement.

    Citation Envoyé par Aleph69 Voir le message
    surtout si tu manipules des classes plus avancées possédant plusieurs paramètres template..
    D'accord sur ce point, mais dans le cas d'une classe template complexe qui le justifie (je l'ai déjà fait aussi). Dans la pratique, ça ne concerne qu'une minorité de classes, ce n'est pas une bonne idée d'en faire une pratique systématique.

    Citation Envoyé par Aleph69 Voir le message
    Pour le fait de réunir les fonctions inline et template dans un seul et unique fichier d'implémentation foo_impl.hpp, c'est effectivement une question que je me pose.
    Le fait de séparer les fonctions inline me paraît intéressant quand on fait beaucoup d'optimisation.
    On voit plus facilement où il n'y a pas d'expansion de code.
    Mais c'est peut-être inutilement compliqué, j'en conviens.
    Franchement, ça ça ne peut se justifier que dans un seul cas : si tu dois avoir plusieurs implémentations différentes en fonctions de la plateforme, et que tu règles ta toolchain pour que ce soit la bonne implémentation qui soit choisie à la compilation. En dehors de ça, ça ne fait que rajouter du code et allonger le temps de compilation, en échange de rien. La séparation n'est que cosmétique, puisque tu dois quand même inclure ton *_impl dans le header principal .

    Pour les friends, sans rentrer dans le débat d'avoir plusieurs friends ou pas, je vois vraiment pas ce que ça apporte à part rendre la gestion des headers plus compliquée pour les utilisateur de la classe. Je vois ça dans du code, je le vire direct.

    Je suis d'accord avec Bousk, tu te compliques la vie. Il faut faire au plus simple et ne rajouter de la complexité que si elle apporte quelque chose. L'éclatement en plein de fichiers, je l'ai vécu sur une grosse base de code, c'est un véritable enfer.
    Find me on github

  5. #5
    Rédacteur/Modérateur


    Homme Profil pro
    Network game programmer
    Inscrit en
    Juin 2010
    Messages
    7 115
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Âge : 36
    Localisation : Canada

    Informations professionnelles :
    Activité : Network game programmer

    Informations forums :
    Inscription : Juin 2010
    Messages : 7 115
    Points : 32 967
    Points
    32 967
    Billets dans le blog
    4
    Par défaut
    Citation Envoyé par Aleph69 Voir le message
    Avec ton approche, tu ne factorises pas ton code.
    Si tu changes le nom de ta classe, si tu ajoutes un paramètre template, etc, tu devras répercuter ta modification partout.
    Euh... gné ?
    Si je change le nom d'une classe, je vais devoir modifier tous les endroits où je déclare cette classe. Je vois pas ce qu'il y a de choquant ni ce que ta "méthode" permet d'éviter ou améliorer.
    Pensez à consulter la FAQ ou les cours et tutoriels de la section C++.
    Un peu de programmation réseau ?
    Aucune aide via MP ne sera dispensée. Merci d'utiliser les forums prévus à cet effet.

  6. #6
    Expert éminent sénior

    Femme Profil pro
    Ingénieur développement logiciels
    Inscrit en
    Juin 2007
    Messages
    5 189
    Détails du profil
    Informations personnelles :
    Sexe : Femme
    Localisation : France, Essonne (Île de France)

    Informations professionnelles :
    Activité : Ingénieur développement logiciels

    Informations forums :
    Inscription : Juin 2007
    Messages : 5 189
    Points : 17 141
    Points
    17 141
    Par défaut
    bah, si ton fichier qui prédéclare la classe Foo s'appelle foo_fwd.hpp et que tu renommes ta classe en Bidule, l'utilisateur s'attend par cohérence avec les autres classes à avoir bidule_fwd.hpp.

    Du coup, il faut renommer le fichier et donc modifier quand même les programmes.

    La seule raison d'avoir un tel fichier, c'est quand tu prédéclares plusieurs classes ensembles.
    C'est ce que fait <iosfwd>.
    Tu remarqueras qu'il n'y a pas de <vector_fwd>, <map_fwd> ou <string_fwd>
    Mes principes de bases du codeur qui veut pouvoir dormir:
    • Une variable de moins est une source d'erreur en moins.
    • Un pointeur de moins est une montagne d'erreurs en moins.
    • Un copier-coller, ça doit se justifier... Deux, c'est un de trop.
    • jamais signifie "sauf si j'ai passé trois jours à prouver que je peux".
    • La plus sotte des questions est celle qu'on ne pose pas.
    Pour faire des graphes, essayez yEd.
    le ter nel est le titre porté par un de mes personnages de jeu de rôle

  7. #7
    Membre expérimenté
    Homme Profil pro
    Chercheur
    Inscrit en
    Mars 2010
    Messages
    1 218
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Localisation : France, Paris (Île de France)

    Informations professionnelles :
    Activité : Chercheur

    Informations forums :
    Inscription : Mars 2010
    Messages : 1 218
    Points : 1 685
    Points
    1 685
    Par défaut
    Bonsoir,

    effectivement, je comprends tout à fait que l'argument du changement de nom de classe n'est pas recevable.
    En revanche, modifier la déclaration d'une classe en ajoutant des paramètres template nécessite bien de répercuter ce changement partout où cette classe fait l'objet d'une déclaration anticipée.
    En la déclarant dans un fichier d'en-tête, je n'ai qu'une seule modification à produire.
    C'est également très intéressant quand on veut éviter d'écrire des déclarations anticipées de classes template un peu complexes; et ce cas est loin d'être rare, en tous cas dans mon domaine (calcul scientifique).

    C'est vrai : la STL n'adopte cette approche que pour <iosfwd>.
    Mais, il y a un historique qui va avec la STL et certains aspects la concernant ne sont pas des modèles de conception à suivre (cf string).
    L'approche dont je vous parle est par exemple adoptée dans Effective C++.
    Je ne me souviens plus des arguments de Meyer mais je vais regarder.

    J'entends tous vos arguments mais je n'arrive pas être convaincu : je trouve l'approche par fichier plus simple et meilleure en terme de factorisation de code.
    Pour les fonctions inline, je suis d'accord que ça complique les choses et je vais sûrement abandonner cette pratique.
    En ce qui concerne les fonctions amies, je suis toujours preneur d'un avis sur le lieu où les définir quand elles sont amies d'au moins deux fonctions.
    Si c'est un problème de conception, je veux bien qu'on m'explique comment écrire un produit entre une matrice et un vecteur.

  8. #8
    Membre émérite
    Avatar de white_tentacle
    Profil pro
    Inscrit en
    Novembre 2008
    Messages
    1 505
    Détails du profil
    Informations personnelles :
    Localisation : France

    Informations forums :
    Inscription : Novembre 2008
    Messages : 1 505
    Points : 2 799
    Points
    2 799
    Par défaut
    Citation Envoyé par Aleph69 Voir le message
    je trouve l'approche par fichier plus simple et meilleure en terme de factorisation de code.
    Juste une petite remarque sémantique. « Factoriser » = regrouper ensemble les facteurs. Séparer en plein de fichiers me semble, sémantiquement parlant, à l’opposé.

    Après, les goûts et les couleurs… Pose-toi quand même la question de la pertinence d’avoir 5 fichiers par classe, et si le rapport gain / coût est vraiment valable. Personnellement, je vois bien le coût, je ne vois pas le gain pour l’instant.

  9. #9
    Membre expert
    Profil pro
    Inscrit en
    Mars 2007
    Messages
    1 415
    Détails du profil
    Informations personnelles :
    Localisation : France, Paris (Île de France)

    Informations forums :
    Inscription : Mars 2007
    Messages : 1 415
    Points : 3 156
    Points
    3 156
    Par défaut
    Citation Envoyé par Aleph69 Voir le message
    En ce qui concerne les fonctions amies, je suis toujours preneur d'un avis sur le lieu où les définir quand elles sont amies d'au moins deux fonctions.
    Si c'est un problème de conception, je veux bien qu'on m'explique comment écrire un produit entre une matrice et un vecteur.
    Déjà, je ne suis pas un partisan du 1 classe == 1 header. Les fonctions amies vont, en règle générale, dans le même header que celui de la classe. Et si jamais une de tes fonctions doit être amie de deux classes, tu choisis comment la convention ? Moi c'est simple, je met tout le monde dans le même header et basta.
    Find me on github

  10. #10
    Expert éminent sénior
    Avatar de koala01
    Homme Profil pro
    aucun
    Inscrit en
    Octobre 2004
    Messages
    11 614
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Âge : 52
    Localisation : Belgique

    Informations professionnelles :
    Activité : aucun

    Informations forums :
    Inscription : Octobre 2004
    Messages : 11 614
    Points : 30 626
    Points
    30 626
    Par défaut
    Salut,

    Deuxième règle de développement SOLID : l'OCP (Open Close Principle) !!!! Ton code doit être ouvert aux évolutions mais fermé au modifications. Ou, si tu préfères, une fois que ton code a été testé, il ne faut plus y toucher à moins que son utilisation n'ait mis un problème en évidence (un "bug" ).

    Si tu veux renommer ta classe, tu dois avoir de très bonnes raisons pour le faire, et, de toutes manières, la déclaration anticipée ne sera pas (et de très loin) le seul endroit où il faudra modifier le nom : une déclaration anticipée permet juste au compilateur de savoir qu'il existe un type dont le nom est indiqué, afin qu'il puisse décider d'accepter la signature d'une fonction prenant le type en question comme paramètre sous la forme d'une référence ou d'un pointeur ou renvoyant une référence (ou un pointeur) sur le type en question.

    Pour chaque déclaration anticipée, tu dois donc t'attendre à devoir également modifier au minimum la signature d'une fonction (quand ce n'est pas de beaucoup plus), non seulement, au niveau de la déclaration de la fonction (de la définition de la classe dans laquelle la fonction apparait), mais aussi au niveau de l'implémentation de la fonction... et donc, également, au niveau des fonctions appelantes.

    Placer une déclaration anticipée unique ne t'apportera absolument rien (car, encore une fois, la déclaration anticipée sera ton moindre problème).

    Pour le reste : on ne décide que très rarement d'ajouter un paramètre template à une classe ou à une fonction. Si l'on prend ce genre de décision, c'est dans le cadre d'une évolution qu'il faut apporter et non dans celui d'une simple modification. Mais le fait d'apporter une évolution au code ne justifie absolument pas que l'on décide de revenir sur des objectifs précédant dores et déjà validé.

    Si tu as développé une classe template qui ne prenait qu'un seul paramètre template, c'est très certainement parce que cet unique paramètre template était correct et suffisant dans l'état des connaissances que tu en avais lors du développement de la classe. Les évolutions qui arrivent par la suite vont sans doute apporter leur lot de complexité supplémentaire, mais, le besoin d'origine qui ne nécessitait qu'un seul paramètre template n'en deviendra pas caduque pour la cause.

    Tu as donc deux solutions : soit tu modifies la classe existante afin de rajouter un paramètre template, mais tu dois alors veiller à rendre ce paramètre supplémentaire optionnel, pour ne pas te trouver face à l'obligation de modifier tout ton code existant, soit tu dois créer -- purement et simplement -- une nouvelle classe correspondant au besoin de ton évolution. Quitte à ce qu'elle s'appuie d'une manière ou d'une autre sur la classe existante

    L'un dans l'autre, la création d'une nouvelle classe destinée à prendre en charge une évolution devrait toujours être préférée à la solution qui consiste à rajouter un paramètre optionnel.

    Enfin, il faut savoir que la directive préprocesseur #include est loin d'être gratuite en termes de temps de compilation. La règle générale est donc simple : il faut essayer d'inclure le stricte minimum dans chaque fichier. En arriver à avoir trois directive d'inclusion (et donc autant de garde anti inclusion multiple) pour le seul plaisir d'avoir un fichier par "type de fonction" spécifique, c'est du pur masochisme
    A méditer: La solution la plus simple est toujours la moins compliquée
    Ce qui se conçoit bien s'énonce clairement, et les mots pour le dire vous viennent aisément. Nicolas Boileau
    Compiler Gcc sous windows avec MinGW
    Coder efficacement en C++ : dans les bacs le 17 février 2014
    mon tout nouveau blog

  11. #11
    Membre expérimenté
    Homme Profil pro
    Chercheur
    Inscrit en
    Mars 2010
    Messages
    1 218
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Localisation : France, Paris (Île de France)

    Informations professionnelles :
    Activité : Chercheur

    Informations forums :
    Inscription : Mars 2010
    Messages : 1 218
    Points : 1 685
    Points
    1 685
    Par défaut
    Bonjour,

    d'accord, je comprends bien vos arguments.
    Je n'ai réellement aucune idée de ce que coûte l'inclusion d'un fichier d'en-tête.
    Est-ce que cette information est connue?

    Par ailleurs, dans l'hypothèse où l'on est partisan d'écrire un fichier par classe, comment fait-on pour traiter le cas des fonctions amies de plusieurs classes?

Discussions similaires

  1. ça se passe comment l'organisation des fichiers source en java
    Par razily dans le forum Entrée/Sortie
    Réponses: 4
    Dernier message: 04/05/2009, 09h45
  2. Organisation des fichiers après génération
    Par mister3957 dans le forum Visual C++
    Réponses: 2
    Dernier message: 14/01/2009, 18h07
  3. [Smarty] Organisation des fichiers et inclusion
    Par Darkcristal dans le forum Bibliothèques et frameworks
    Réponses: 3
    Dernier message: 23/12/2008, 17h46
  4. [Système] Organisation des fichiers
    Par Prosis dans le forum Langage
    Réponses: 12
    Dernier message: 10/02/2008, 23h30
  5. Organisation des fichiers du programme
    Par greg13 dans le forum Linux
    Réponses: 2
    Dernier message: 16/03/2007, 15h25

Partager

Partager
  • Envoyer la discussion sur Viadeo
  • Envoyer la discussion sur Twitter
  • Envoyer la discussion sur Google
  • Envoyer la discussion sur Facebook
  • Envoyer la discussion sur Digg
  • Envoyer la discussion sur Delicious
  • Envoyer la discussion sur MySpace
  • Envoyer la discussion sur Yahoo