Partage
  • Partager sur Facebook
  • Partager sur Twitter

Architecture review projet c++

    2 août 2022 à 13:27:50

    Bonjour,

    Je développe maintenant en c++ depuis plusieurs années, toutefois, j'ai rarement eu l'occasion de réellement faire de la conception/architecture. Actuellement je suis sur un projet qui utilise le compilateur clang avec c++17, et au fur et à mesure que je développe je me rend bien compte que l'architecture de base ne conviens pas.

    Je voulais savoir si vous aviez des bon conseils a me donner par rapport à sa? Actuellement pour donner un aperçu du projet, je vais poster une image du diagramme d'include du projet, je sais que ce n'est pas le meilleur aperçu. Si jamais vous savez comment avoir de meilleur aperçu?

    Pour donner une explication du projet, il est composé d'une librairie partagée et d'un executable:

    Parmis les nombreuses questions que j'ai:

    Actuellement, j'ai déplacé la fonction main dans la librarie partagée.Elle est actuellement dans le fichier entry.h. Pour ne pas avoir a me soucier du point d'entrée. On m'a conseillé de le laissé dans un fichier .cpp, néanmoins j'obtiens rapidement des erreurs concernant le point d'entrée dans ce cas. Est-ce que c'est une bonne idée? y a-t-il de meilleur manière de faire? (Pour faire ça, je me suis inspirée de: https://github.com/TheCherno/Hazel/blob/master/Hazel/src/Hazel/Core/EntryPoint.h)

    Ma class Application aujourd'hui regroupe plusieurs élement: Window et Basegame, Window me permet de créer une fenêtre et de gérer les différentes plateforme (Linux, windows), alors que basegame est la classe parente de my_game crée dans l'executable. Ca me permet d'appeler différente fonction créer dans l'executable(initialize, update, render) au sein de ma librairie.

    Toutefois, j'arrive au problème qui m'a fait remettre en cause mon architecture, je dois gérer les input. Pour cela, j'ai une classe présente dans le fichier Input qui va trigger des event a l'aide d'un dispatcher(de la librairie entt, https://github.com/skypjack/entt/wiki/Crash-Course:-events,-signals-and-everything-in-between#event-dispatcher). Mais cette classe Input dois etre disponible pour Window et pour basegame or ce sont les classe les plus éloignés de mon architecture.

    J'ai pensé utilisé davantage des éléments statique, mais ma classe Input prend en charge un dispatcher qui n'est pas static pour réaliser les event. Et je me vois mal mettre beaucoup de code en static (si je me souviens bien c'est une mauvaise pratique) 

    Est-ce que vous avez des idées? Ou bien simplement de la lecture sur le sujet?

    -
    Edité par SébastienRoth 2 août 2022 à 13:35:24

    • Partager sur Facebook
    • Partager sur Twitter
      15 août 2022 à 21:00:27

      Difficile d'évaluer une architecture rapidement.

      Mais mettre le code du main dans un header, cela me fait tiquer aussi.

      Un header peut être inclus dans plusieurs unités de compilation et donc poser des problèmes au moment de l'édition de lien.

      Si vous avez des problèmes si vous le mettez dans un .cpp, c'est peut-être qu'il y a un ou plusieurs problèmes de conception dans votre code dont vous n'avez pas conscience (celui que vous avez détecté en est peut-être l'un d'eux).

      Pouvez-vous nous indiquer les erreurs que vous obtenez si vous mettez ce code dans un cpp ?

      Du peu que vous nous présentez, j'ai l'impression que la "librairie" est bien plus un framework qu'une librairie "additionnelle", car elle semble imposée grandement comment doit être codé l'exécutable.

      Si c'est le cas, j'inverserais les dépendances et faire du framework l'exécutable et le "jeu" un "plug-ins/add-ins" du framework.

      Ainsi, on sait qui mène la danse, la pompe à message/à événement est dans l'exécutable.

      Quel est votre découpage en module ?

      Quelles sont les interfaces à implémenter pour changer d'OS, pour changer de librairie graphique ?

      • Partager sur Facebook
      • Partager sur Twitter
      Je recherche un CDI/CDD/mission freelance comme Architecte Logiciel/ Expert Technique sur technologies Microsoft.
        15 août 2022 à 21:39:07

        Je vais avoir du mal a évaluer tout ça, entre ma question et maintenant j'ai recommencé beaucoup de chose depuis le début estimant que ce que j'avais commencé était très fouillis. Actuellement j'ai décidé de laissé le main dans mon executable et d'appeler mon moteur en lui passant le jeu crée: 

        Comme ceci:

        int main(int argc, char* argv[])
        {
        	Engine::Engine eng{};
        	eng.Run<my_game>(argc, argv); //Engine vient de la librairie et my_game de l'executable
        	return EXIT_SUCCESS;
        }

        Toutefois même si je ne peux pas répondre concernant les erreurs spécifiques du main. Je suis d'accord que la librairie que j'essaie de construire est un indispensable pour l'executable, il ne peut pas fonctionner sans le moteur si c'est ce que vous voulez dire.

        Mais je ne vois pas comment inverser la dépendances? Le moteur ici reste une source principal de fonction qui sera utilisé par le jeu. Alors comment est-ce que je pourrais rendre ma librairies un executable? (quelle point d'entrée donné?) et si ma librairie devient executable, ça veut dire que le jeu devient une simple librairie?

        Je n'ai jamais créer de pompe a message, donc le concept peut être assez flou pour moi ici présent. Actuellement j'utilise la librairie GLFW3 pour créer des fenêtre sur windows et Linux et actuellement je cherche a développer le moteur uniquement sur Vulkan. Ce sont mes premiers objectifs.

        Concernant mon découpage, et bien, il y a uniquement ma librairie contenant actuellement l'entièreté du moteur (logging, input, audio, graphic etc) et l'executable rassemblant tout le code source propre au Gameplay. Je n'avais pas l'impression qu'il était nécessaire de faire un découpage plus complexe?

        • Partager sur Facebook
        • Partager sur Twitter
          15 août 2022 à 22:14:10

          bacelar a écrit:

          Si c'est le cas, j'inverserais les dépendances et faire du framework l'exécutable et le "jeu" un "plug-ins/add-ins" du framework.

          Ça c'est vraiment une très bonne idée, d'ailleurs vous allez rigoler mais le seul endroit sur Terre où j'ai entendu quelqu'un parler de cette méthode c'est ici https://guide.handmadehero.org/code/day011/#2671 

          Parce qu'on voit tout le monde coder leur jeu d'une manière qu'il se retrouve à appeler directement la partie plus bas niveau (soit directement (berk) soit via une abstraction style SDL). Alors que là l'idée c'est que le jeu n'appelle pas directement l'OS, c'est l'OS qui appelle le jeu. Si un jour vous voulez virer SDL ou GLFW ou autre, vous allez être coincé si vous l'appeler directement. Alors que si c'est la couche bas niveau qui se "branche" sur le jeu, dans ce cas on peut la changer facilement, sans que le jeu en dépende

          • Partager sur Facebook
          • Partager sur Twitter
            15 août 2022 à 23:30:27

            JadeSalina a écrit:

            Ça c'est vraiment une très bonne idée, d'ailleurs vous allez rigoler mais le seul endroit sur Terre où j'ai entendu quelqu'un parler de cette méthode c'est ici https://guide.handmadehero.org/code/day011/#2671 


            Voilà , ça cite encore Casey...
            C'est fou cette obsession oublie le parce que je ne le supporte plus d'en entendre parler de ta part !


            JadeSalina a écrit:

            Parce qu'on voit tout le monde coder leur jeu d'une manière qu'il se retrouve à appeler directement la partie plus bas niveau (soit directement (berk) soit via une abstraction style SDL). Alors que là l'idée c'est que le jeu n'appelle pas directement l'OS, c'est l'OS qui appelle le jeu. Si un jour vous voulez virer SDL ou GLFW ou autre, vous allez être coincé si vous l'appeler directement. Alors que si c'est la couche bas niveau qui se "branche" sur le jeu, dans ce cas on peut la changer facilement, sans que le jeu en dépende

            Sinon non Casey n'a pas inventé le multi plateforme...

            Ensuite ça dépend ce que tu vise comme objective, si tu veux faire du multiplate-forme , faut mieux appeler du bas niveau un moment , je fais mon moteur il est multiplate-forme (PC et console) et sur console , ça tape directement le hardware sans lib et sur les consoles que je vise y'a pas d'OS.

            Donc non la SDL ou GLFW ne bloque pas, preuve je le fait très bien :)
            si tu veux que ton jeu ne dépend pas du "bas niveau" UE4 est un exemple , mais en gros un ensemble de fonctionnalité qui marchera peu importe la plateforme , ça sera celui qui fait son moteur d’arranger son code pour chaque plate forme cible.


            -
            Edité par HelbaSama 15 août 2022 à 23:32:22

            • Partager sur Facebook
            • Partager sur Twitter
              16 août 2022 à 2:03:48

              Saint Casey, priez pour nous ...
              • Partager sur Facebook
              • Partager sur Twitter

              Le Tout est souvent plus grand que la somme de ses parties.

                16 août 2022 à 2:08:35

                HelbaSama a écrit:

                Sinon non Casey n'a pas inventé le multi plateforme...

                Oui mais tout le monde a l'idée de faire le haut niveau qui appelle le bas niveau alors que lui il a eu l'idée de faire le contraire. Vous avez regardé l'extrait ? Sur la droite il y a le chapitrage, vous pouvez aller voir par vous même ce qu'il veut dire par là. La façon classique d'avoir le jeu qui appelle l'OS c'est le "style 1" et l'inverse c'est le "style 2"

                HelbaSama a écrit:

                Donc non la SDL ou GLFW ne bloque pas, preuve je le fait très bien :)

                Si vous appeler depuis le jeu une fonction "foo()" de GLFW, et que par malheur GLFW ne supporte pas votre platforme cible, comment vous faites ? Un truc du genre 

                void abcd()
                {
                  #ifdef CONSOLE
                    consoleFoo();
                  #else
                    glfwFoo();
                  #endif
                }

                ?

                Et je cites Casey car il dit des choses intéressantes tout simplement, il n'y a pas de "gourou" ou je ne sais quoi, une fois j'ai cité Scott Meyers, alors que au contraire Casey dit que "lesson 1 of programming is : don't listen to a Scott Meyer talk" (https://guide.handmadehero.org/code/day286/#4142)

                -
                Edité par JadeSalina 16 août 2022 à 2:12:32

                • Partager sur Facebook
                • Partager sur Twitter
                  16 août 2022 à 4:31:27

                  > Oui mais tout le monde a l'idée de faire le haut niveau qui appelle le bas niveau alors que lui il a eu l'idée de faire le contraire

                  Il n'a rien inventé, c'est le principe même d'un framework: se brancher sur des "événements" pour que le framework s'occupe de toute la partie bas niveau et appel le code utilisateur.

                  • Partager sur Facebook
                  • Partager sur Twitter
                    16 août 2022 à 7:11:39

                    JadeSalina a écrit:

                    Si vous appeler depuis le jeu une fonction "foo()" de GLFW, et que par malheur GLFW ne supporte pas votre platforme cible, comment vous faites ? Un truc du genre 

                    void abcd()
                    {
                      #ifdef CONSOLE
                        consoleFoo();
                      #else
                        glfwFoo();
                      #endif
                    }

                    ?



                    Ce n'est pas ce que je fais ,je créer juste plusieurs dossier avec du code source dedans , ça me permet quand je vise une nouvelle plateforme que le compilateur me dise si j'ai oublié ou pas une fonctionnalité à implémenté ou pas.
                    Cette méthode de faire des ifdef ,je ne l'apprécie pas, vu que ben , si tu oublie d'implémenté un truc, c'est problématique (en plus de faire du code moche).
                    Je dois le faire qu'une fois pour gérer la SDL1 et SDL2 parce que je voyais pas l’intérêt de créer un autre fichier pour 2-3 fonctions.

                    JadeSalina a écrit:

                    Oui mais tout le monde a l'idée de faire le haut niveau qui appelle le bas niveau alors que lui il a eu l'idée de faire le contraire. Vous avez regardé l'extrait ? Sur la droite il y a le chapitrage, vous pouvez aller voir par vous même ce qu'il veut dire par là. La façon classique d'avoir le jeu qui appelle l'OS c'est le "style 1" et l'inverse c'est le "style 2"




                    Alors comme dit plus haut ce n'est pas nouveau , pas mal de framework le font , il n'a pas inventél 'eau chaude.

                    Si je précise ma méthode ,c'est que tu parle souvent de "bas niveau" et de from scratch , mais si t'as pas d'OS ,tu fais quoi ?
                    Tu recréer un OS pour faire ton machin ?
                    • Partager sur Facebook
                    • Partager sur Twitter
                      16 août 2022 à 11:26:24

                      Wouah, beaucoup de réponses d'un coup ^^

                      J'ai regardé les exemple de Casey du coup. Et est-ce qu'il y a vraiment beaucoup d'avantages? Je veux dire par la qu'au final, actuellement j'utilise GLFW, mais si je passais sur du natif avec imaginons du multiplateforme, windows, linux, mac. Il faudrait que j'intègre la boucle de rendu dans l'os pour chaque plateforme non?

                      Appeler des fonctions que ce soit venant de GLFW ou de fonction native reste relativement rare (principalement l'input polling). Ce n'est pas comme si on mélangeait toutes les fonctions des librairies non? (dites moi si je me trompe, il y a peut être plus davantage que je n'en vois)

                      • Partager sur Facebook
                      • Partager sur Twitter
                        16 août 2022 à 13:03:00

                        Salut,

                        SébastienRoth a écrit:

                        Concernant mon découpage, et bien, il y a uniquement ma librairie contenant actuellement l'entièreté du moteur (logging, input, audio, graphic etc) et l'executable rassemblant tout le code source propre au Gameplay. Je n'avais pas l'impression qu'il était nécessaire de faire un découpage plus complexe?

                        En fait, c'est là qu'est le véritable problème.

                        Car, le fait est que, quand tout est regroupé, tu en arrives très rapidement à une situation dans laquelle tout peut dépendre littéralement ... de n'importe quoi.

                        Par exemple, tu peux vouloir baser la logique qui permet au joueur d'interagir sur quelque chose si un son particulier est justement en cours de production, ou, plus vraisemblablement, si un son bien particulier vient justement d'avoir été "complètement joué".  Simplement parce que tu as la possibilité de le faire.

                        Dans le schéma que tu nous montre, on remarque que beaucoup de ligne vont "directement" d'un fichier (appelons le "origine") à l'autre (appelons le "destination)  alors qu'il existe un "chemin détourné" qui fait que le fichier de destination est d'office inclut dans le fichier d'origine.

                        je vais certainement en oublier, mais, si tu regarde bien:

                        • entt/entt.hpp est inclu directement dans BaseGame.h alors qu'il est inclut indirectement par Core/input.h
                        • KeyMouseCode.h est inclu directement dans  Core/input.h alors qu'il est inclu indirectement par event.h
                        • defines.h est inclu directement dans BaseGame.h alors qu'il est inclu indirectement par Core/input.h
                        • defines.h est inclu directement dans Core/Application.h alors qu'il est inclu indirectement par BaseGame.h (par Core/Input qui est inclu par BaseGame.h)
                        • stdint.h et sstream sont inclusdirectement dans Core/ApplicationProps.h alors qu'ils sont inclus indirectement par defines.
                        • Core/ApplicationProps.h est inclus directement par Core/Application.h, alors qu'il est inclu indirectement par platfor/Window.h et par BaseGame.h
                        • Core/Log.h est inclu directement dans entry.h alors qu'il est inclu indirectement par my_game.h et par platform/Window.h

                        Hé bien tu pourrais déjà grandement simpliifier ton schéma en supprimant les incusions directes à chaque fois qu'une inclusion indirecte fournit déjà le fichier.

                        Si cela ne changera effectivement rien au problème, cela te permettrait sans doute de te faire "plus facilement" une idée des relations qui existent entre les différents fichiers, tout en permettant à ton projet de continuer à compiler ;)

                        Ensuite, il y a des choses qui, même en l'état, ne me semble pas forcément tout à fait logiques, comme la trinité Core/Application.h + Core/ApplicationProps.h + platform/Window.h.

                        Car Core/Application.h et Core/ApplicationProps.h font, de toute évidence, partie du "même ensemble". Il est donc "assez logique" de considérer que le fichier qui expose la notion "la plus complexe" (à savoir Core/Application.h) dépende de celui qui expose la notion "la moins complexe" (à savoir Core/ApplicationProps.h).

                        Par contre, platform/Window.h ne fait clairement pas partie de cet ensemble "Core". Soit parce paltform fait partie des dépendances de Core, et qu'il ne peut donc en toute logique pas dépendre de Core, soit parce qu'il s'agit d'un "sous ensemble particulier" de Core (mais le fichier serait alors Core/platform/Window.h).

                        Quoi qu'il en soit, tu essaye de "remonter le courant" en rendant platform/Windows.h dépendant de Core/ApplicationProps.h. Et ce n'est jamais une bonne chose.

                        Un bon exercice pour t’entraîner à inverser les dépendances serait peut-être de commencer par cette fameuse trinité : Comment pourrais tu t'y prendre pour que Core/Application.h dépende directement de Core/ApplicationProps.h mais que plateform/Window.h puisse fonctionner sans même avoir à connaitre ce qui se trouve dans Core/ApplicationProps.h?

                        Peut-être devras-tu rajouter des fonctionnalités qui ne manipulent que des types de données de base dans platform/windows.h et qui te permettront de remplir tes données exposées dans Core/ApplicationProps.h, mais cela te permettra au moins de "briser le cercle" ;)

                        • Partager sur Facebook
                        • Partager sur Twitter
                        Ce qui se conçoit bien s'énonce clairement. Et les mots pour le dire viennent aisément.Mon nouveau livre : Coder efficacement - Bonnes pratiques et erreurs  à éviter (en C++)Avant de faire ce que tu ne pourras défaire, penses à tout ce que tu ne pourras plus faire une fois que tu l'auras fait

                        Architecture review projet c++

                        × Après avoir cliqué sur "Répondre" vous serez invité à vous connecter pour que votre message soit publié.
                        × Attention, ce sujet est très ancien. Le déterrer n'est pas forcément approprié. Nous te conseillons de créer un nouveau sujet pour poser ta question.
                        • Editeur
                        • Markdown