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 :

héritage et chaine d'objets


Sujet :

C++

  1. #41
    Membre éclairé

    Profil pro
    Inscrit en
    Décembre 2013
    Messages
    393
    Détails du profil
    Informations personnelles :
    Localisation : France

    Informations forums :
    Inscription : Décembre 2013
    Messages : 393
    Points : 685
    Points
    685
    Par défaut
    Citation Envoyé par mintho carmo Voir le message
    Je pense que tu te méprends sur weak_ptr. Lorsque tu fais un lock, tu obtiens un shared_ptr. Si ce shared_ptr est valide, cela veut dire que le premier shared_ptr (le propriétaire) n'a pas encore détruit l'objet. Et s'il souhaite ensuite le détruire, tu as un shared_ptr dessus, tu as donc la garantie que l'objet ne sera pas détruit tant que tu conserves ce shared_ptr.

    En bref, tu es sens utiliser un weak comme ca :
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    6
    7
    8
    void f() {
        auto sp = wp.lock();
        if(sp) {
            // ici, tu utilises ton objet et tu es sur qu'il ne sera pas détruit pendant son utilisation
        } else {
            // l'objet est détruit, tu ne peux pas l'utiliser. Il faut créer un nouvel objet
        }
    }
    Ce code est safe en termes mémoire. Il n'y a pas d’équivalent safe avec une autre approche

  2. #42
    Membre habitué
    Homme Profil pro
    Étudiant
    Inscrit en
    Octobre 2014
    Messages
    521
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Âge : 36
    Localisation : France, Hérault (Languedoc Roussillon)

    Informations professionnelles :
    Activité : Étudiant

    Informations forums :
    Inscription : Octobre 2014
    Messages : 521
    Points : 136
    Points
    136
    Par défaut
    Si c'est bien une approche comme celle que j'ai dit plus haut, il y quelque chose qui me paraît un peut lourd :

    si j'ai 10 objets qui bougent en même temps, il va falloir que chacun de ces objets signalent qu'ils bougent. Lorsqu'ils l'ont tous fait, il va falloir que dans le système Drawing, par ex, chaque numéro concerné ( ici tous ) en soit informé, pour se repositionner au bon endroit...

    C'est bien ca ?

    Donc ça fait qu'on parcourt et reparcourt les système tt le temps et avec des informations, comme par ex ici des coordonnées, pour pas grand chose... ( mais je supose que c'est pas un pb..),

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

    Informations professionnelles :
    Activité : aucun

    Informations forums :
    Inscription : Octobre 2004
    Messages : 11 612
    Points : 30 612
    Points
    30 612
    Par défaut
    Citation Envoyé par mintho carmo Voir le message
    @koala01

    Je te rappelle que c'est une discussion que l'on a déjà eu

    Je reprends le problème a l'envers.

    Tu dis que l'on peut utiliser un reference_wrapper, il "suffit" de garantir que l'objet partage a une durée de vie plus grande que les pointeurs.
    Oui, je persiste et signe sur ce point
    Premièrement, si on pouvait toujours apporter cette garantie, on n'aurait jamais de problème de dangling pointer. C'est loin d’être le cas. (Peu importe que ce soit un problème de conception. Le problème existe)
    Justement, non... Car, s'il n'y avait pas ce problème de conception à la base (qui est responsable de quoi, qui doit donc avoir la priorité sur quoi, qui est "le maitre" et qui est "l'esclave", tournes cela comme tu veux), tu n'aurais simplement pas à t'en faire des danding pointers...
    Je travaille avec des idiots (dont moi-même, dans quelques mois quand je relirais mon code). Ces idiots peuvent ne pas respecter cette contrainte sur la durée de vie de mon objet partagé. Je veux donc un moyen qui me permet (dans le thread qui utilise cet objet) de détecter une erreur dans le thread propriétaire. Quelle solution apporte quelle garantie ?

    - pointeur + reference_wrapper = dangling
    - unique + raw = dangling
    - unique + reference sur unique = n'empêche pas la destruction en cours d'utilisation de l'objet
    La soluton est simple: fait en sorte qu'ils n'aient pas le choix, en t'assurant que l'objet qui contient tes unique_ptr existe plus longtemps que n'importe quel thread susceptible de les utiliser... Mais bien sur, cela pose un gros problème, car il faut arriver à la base à déterminer qui sera effectivement le responsable de la durée de vie de ces pointeurs, et, plus dur encore, faire en sorte que ce soit sa seule et unique responsabilité.

    Manque de pot, il faut accepter l'idée que la conception n'est pas qu'une série de diagrammes inutiles car personne n'y comprend de toutes manières rien... Chose que tu n'as semble-t-il pas encore forcément intégrée
    Le shared + weak évite le dangling. Et pour utiliser l'objet dans mon thread, je dois faire un lock, ce qui me permet de devenir propriétaire temporaire, le temps d'utilisation de l'objet, et donc garantir aussi que l'objet n'est pas détruit en cours d'utilisation.
    Et, à ton avis, ton lock, il fait quoi
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    return expired()? shared_ptr<T>() :shared_ptr<T>(*this);
    C'est inline, ca ne veut pas dire que ce sera forcément une instruction atomique pour la cause... Et donc, la question reste posée : tant que tu n'as qu'un seul propriétaire "officiel et permanent" de ton pointeur, que se passera-t-il selon toi si ce propriétaire décide de détruire le pointeur entre le test et le renvoi du pointeur

    (On est d'accord que le shared dans le thread propriétaire ne va pas prendre en compte le weak s'il doit détruire l'objet. Mais peut importe, je ne recherche pas la garantie que l'objet n'est pas détruit avant de l'utiliser dans mon thread - si c’était le cas, on serait dans le cas de propriétaires multiples et on aurait plusieurs sahred - mais la garantie que s'il n'existe pas, je le saurais. L'autre garantie que je recherche - la non-destruction pendant utilisation - est apportée par le lock)
    Et le seul avantage que tu auras, ce sera de déréférencer nullptr au lieu d'une adresse invalidée... Bref, tu choisi entre la peste et le choléras, parce que cela t'occasionnera toujours un comportement indéfini

    Et même le test de validité de ton pointeur ne pourrait de toutes facons pas te sortir de l'auberge (raison pour laquelle j'ai parlé de déréférencer nullptr )
    On est d'accord qu'une bonne conception est la meilleure approche. Mais un code robuste permettra de vérifier si quelqu'un fait une erreur et ne respecte pas la conception. Sans cela, tu n'es pas a l'abris d'une erreur et ton code n'est pas robuste.
    Justement, le seul moyen d'avoir un code robuste, c'est d'avoir une conception sans faille : tu veux que "quelque chose" soit "au dessus de la melée des threads"? Place le avant la construction des threads, et fait en sorte qu'il reste au dessus de la mélée... l'utilisateur n'aura pas d'autre choix que... de respecter la manière dont les choses sont sensées se passer (c'est bizard cette impression que je commence une discussion que j'aurais très bien pu avoir au sujet de notions comme l'anti-patern singleton )
    Donc, je pense que shared apporte plus de garantie que unique et peut être utilisé aussi lorsque l'on a un propriétaire unique.
    Comme je l'ai dit, je suis loin d'en être persuadé ... Mais bon, comme il semble que l'on ait chacun son point de vue et aucun argument susceptible de nous mettre d'accord, je te laisserai tes shared_ptr et je continuerai avec mes unique_ptr
    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

  4. #44
    Expert confirmé
    Homme Profil pro
    Étudiant
    Inscrit en
    Juin 2012
    Messages
    1 711
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Localisation : France

    Informations professionnelles :
    Activité : Étudiant

    Informations forums :
    Inscription : Juin 2012
    Messages : 1 711
    Points : 4 442
    Points
    4 442
    Par défaut
    Citation Envoyé par koala01 Voir le message
    Et, à ton avis, ton lock, il fait quoi
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    return expired()? shared_ptr<T>() :shared_ptr<T>(*this);
    C'est inline, ca ne veut pas dire que ce sera forcément une instruction atomique pour la cause... Et donc, la question reste posée : tant que tu n'as qu'un seul propriétaire "officiel et permanent" de ton pointeur, que se passera-t-il selon toi si ce propriétaire décide de détruire le pointeur entre le test et le renvoi du pointeur
    Citation Envoyé par http://en.cppreference.com/w/cpp/memory/weak_ptr/lock
    Effectively returns expired() ? shared_ptr<T>() : shared_ptr<T>(*this), executed atomically.

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

    Informations professionnelles :
    Activité : aucun

    Informations forums :
    Inscription : Octobre 2004
    Messages : 11 612
    Points : 30 612
    Points
    30 612
    Par défaut
    Citation Envoyé par mintho carmo Voir le message
    Je pense que tu te méprends sur weak_ptr. Lorsque tu fais un lock, tu obtiens un shared_ptr. Si ce shared_ptr est valide, cela veut dire que le premier shared_ptr (le propriétaire) n'a pas encore détruit l'objet. Et s'il souhaite ensuite le détruire, tu as un shared_ptr dessus, tu as donc la garantie que l'objet ne sera pas détruit tant que tu conserves ce shared_ptr.

    En bref, tu es sens utiliser un weak comme ca :
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    6
    7
    8
    void f() {
        auto sp = wp.lock();
        if(sp) {
            // ici, tu utilises ton objet et tu es sur qu'il ne sera pas détruit pendant son utilisation
        } else {
            // l'objet est détruit, tu ne peux pas l'utiliser. Il faut créer un nouvel objet
        }
    }
    Ce code est safe en termes mémoire. Il n'y a pas d’équivalent safe avec une autre approche
    Je suis d'accord, à un point près :
    ettons que thread1 soit l'unique propriétaire (pour l'instant) d'un shared_ptr et que thead2 dispose d'un weak_ptr vers l'objet sous-jacent. Tu es d'accord avec moi : shared_ptr ne tient aucun compte du weak_ptr, sa fonction unique() renvoie donc true.

    Théoriquement, le destructeur de shared_ptr va utiliser une logique proche de
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    if(unique()){
        delete object;
    }else{
        --count;
    }
    Et, d'un autre coté, quand tu fais lock dans trhead2, cela va fonctionner sous la forme d'un test proche de
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    if(expired()){
        return std::shared_ptr<T>(); // un pointeur vide, tout nouveau, tout propre, sans rapport avec l'objet sous-jacent
    }else{
        return std::shared_ptr<T>(*this); // c'est ici que unique passe à false parce que count() passe à 1!!!
    }
    Jusque là, je présumes que nous sommes toujours bien d'accord, n'est-ce pas?

    En théorie, tout devrait bien se passer, sauf que... les fonctions de weak_ptr ne sont nullement atomiques... Résultat des courses, quand thread2 obtient le résultat de expired(), cet état peut, justement, être modifié par thread1 (qui, lui, aura des opérations atomiques) AVANT que thread2 n'ait "eu le temps" de renvoyer le shared_ptr avec son objet sous-jascent.

    Du coup, je maintient : tu auras des garanties en multi-thread que si tu as déjà la garantie de ne pas être face à un propriétaire unique lorsque tu envisage de passer par un weak_ptr
    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

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

    Informations professionnelles :
    Activité : aucun

    Informations forums :
    Inscription : Octobre 2004
    Messages : 11 612
    Points : 30 612
    Points
    30 612
    Par défaut
    Citation Envoyé par mazertys17 Voir le message


    merci ! Ça a en effet l'air d'être clairement le niveau supérieur...

    Si je te suis bien, ça pourrait ressembler à ca :

    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
     
     
    void Engine::load()
    {
           _number ++ ;
     
           //là on charge les donnés depuis le disque, par exemple, 
           //et construit les modules demandé pour les mettre enssuite ds les systèmes :
     
           _drawing.add ( _number , _graphics ) ; //ou Graphics serait l'équivalent de ce que j'ai dit plus haut...
           _physics.add ( _number, _collision );
     
           //etc...
     
    }
     
    void Engine::update()
    {
           _drawing.update() ;
           _physics.update() ;
           _sound.update() ;
           //etc...
    }
    Suis-je sur la bonne piste ?
    Oui, c'est l'idée, en effet, même si, d'une certaine manière, c'est encore pire que cela
    Citation Envoyé par mazertys17 Voir le message
    Si c'est bien une approche comme celle que j'ai dit plus haut, il y quelque chose qui me paraît un peut lourd :

    si j'ai 10 objets qui bougent en même temps, il va falloir que chacun de ces objets signalent qu'ils bougent. Lorsqu'ils l'ont tous fait, il va falloir que dans le système Drawing, par ex, chaque numéro concerné ( ici tous ) en soit informé, pour se repositionner au bon endroit...

    C'est bien ca ?
    Presque oui
    Donc ça fait qu'on parcourt et reparcourt les système tt le temps et avec des informations, comme par ex ici des coordonnées, pour pas grand chose... ( mais je supose que c'est pas un pb..),
    En fait, d'une certaines manière, c'est presque encore pire que cela...

    Imagine un système "Draw" qui ne fait qu'une seule et unique chose sur tout le temps qu'il existe : parcourir --encore et encore-- tous les éléments d'une collection pour les afficher (moyennant peut être quelques calculs pour pouvoir sélectionner ceux qui doivent effectivement être affichés) et qui, une fois qu'il a fini d'afficher tous les éléments de cette collection, reprend au début de la collection et recommence.

    Imagine un autre système -- mettons Physics -- qui ne fait encore qu'une seule et unique chose : parcourir tous les éléments d'une collection pour calculer leur position actuelle (disons à l'instant T+1) en fonction de différentes données (il marche ou court dans telle direction, il est à l'arrêt, il ....) et bien sur, de la position que chaque élément occupait "juste avant" (disons, à l'instant T)

    Imagine un troisième système -- mettons "keyboard" qui va se contenter de vérifier encore et encore quelles touches du clavier sont enfoncées afin de mettre à jour l'état de l'objet le plus important du jeu : l'avatar du joueur.

    Imagine un quatrième système -- mettons AI -- qui va calculer l'état des monstres, puis un cinquième -- mettons Lan -- qui va recevoir les modifications d'états apportées à d'autres joueurs, puis un sixième système, un septième, un... bah,tu peux avoir autant de systèmes que nécessaire en fait. Mais le principe est toujours le même : une fois que tous ces systèmes sont chargés, il ne font qu'une seule et unique chose : boucler pour "mettre à jour" tous les éléments qu'ils contiennent et, une fois fini, il recommencent depuis le début.

    La seule chose, c'est qu'il faut de temps en temps que les systèmes communiquent entre eux. le point impératif est que ces communications restent les plus simples possibles : un id représentant l'objet concerné par la comm, et quelques données "simples" comme une positon (un point, ca se transmet vite), une vitesse, un "azimut", une énumération "assis/debout/couché", une autre énumération "marcher/courir/arret", j'en passe et de meilleurs.

    Toutes ces "communications" sont placées dans un "pipe" (en fait, un grosse file d'événements) pour être émises en permanence (ca fait quoi ca, notre ...8eme, notre 15eme système ) à destination de... tout système intéressé par la communication en question.

    C'est relativement difficile à expliquer comme cela -- surtout sans préparation -- mais, l'idée est vraiment que chaque système puisse travailler sur ses propres données, de manière à pouvoir boucler indéfiniment (et en toute indépendance!!!) sur ses données. Bien qu'il y aura forcément quelques données communes (physics et Draw auront sans doute une série de positions communes pour chaque objet, ia partagera sans doute d'ailleurs aussi ces positions, pour déterminer quand le joueur passe "assez près du monstre" pour que le monstre décide de l'attaquer, ...), et c'est ton role de définir lesquelles en fonctions de tes propres besoins
    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

  7. #47
    Membre éclairé

    Profil pro
    Inscrit en
    Décembre 2013
    Messages
    393
    Détails du profil
    Informations personnelles :
    Localisation : France

    Informations forums :
    Inscription : Décembre 2013
    Messages : 393
    Points : 685
    Points
    685
    Par défaut
    Citation Envoyé par koala01 Voir le message
    Justement, non... Car, s'il n'y avait pas ce problème de conception à la base
    Une bonne conception ne tient que jusqu'à ce qu'un développeur ne la respecte pas. Et c'est pas faute d'écrire du code clair, commenté, documenté, etc.

    Donc on est d'accord sur le fond : si on a la garantie d'avoir une bonne conception, il n'y a pas de problème. Malheureusement, cette garantie dépend de la garantie qu'aucun développeur ne fait d'erreur. Tu es pris en flagrant délits de bisounourisme ( ). Dans le monde réel, la seconde garantie n'est pas possible à avoir sur des gros projets à long termes, donc la première non plus.

    Citation Envoyé par koala01 Voir le message
    Chose que tu n'as semble-t-il pas encore forcément intégrée
    Ne t'en fait pas pour moi niveau conception (bien que j'ai toujours à apprendre). Depuis des années que l'on discute ensemble sur ce forum et d'autres, tu devrais le savoir depuis le temps que j'ai une attention particulière sur la conception...

    Mon propos n'est pas d'utiliser shared_ptr à la place d'une bonne conception (si c'était le cas, je dirais d'utiliser des shared_ptr partout au lieu des weak_ptr et et je n'aurais pas approuvé l'importance d'avoir un propriétaire unique)
    Juste qu'une conception peut ne pas être respecter par erreur, et si c'est le cas, je veux des garanties que cette erreur sera détecter (de préférence avec un bel assert et un message d'insulte pour le développeur qui a fait l'erreur).

    Donc entre le fait tu as trop confiance dans les autres développeurs et que te trompes sur lock (qui est explicitement garantie dans la norme d'être atomique), je pense effectivement que tu te trompes sur ce coup là (pour une fois )

  8. #48
    Membre habitué
    Homme Profil pro
    Étudiant
    Inscrit en
    Octobre 2014
    Messages
    521
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Âge : 36
    Localisation : France, Hérault (Languedoc Roussillon)

    Informations professionnelles :
    Activité : Étudiant

    Informations forums :
    Inscription : Octobre 2014
    Messages : 521
    Points : 136
    Points
    136
    Par défaut
    Merci, Koala, pour toutes tes explications
    ( je crois que je vais en effet revoir tout mon system ).

    Une question : ce système nécessite-t-il des compétences avancés en programmation pour être utilisé correctement ( comme le multi-tread ou autre ) ?

    Est-ce un système utilisé fréquemment pour les jeux vidéo, et selon vous adapté à un jeu type str ?

    Bien qu'il y aura forcément quelques données communes (physics et Draw auront sans doute une série de positions communes pour chaque objet, ia partagera sans doute d'ailleurs aussi ces positions, pour déterminer quand le joueur passe "assez près du monstre" pour que le monstre décide de l'attaquer, ...)
    donc ça impliquerait un ascenseur non ? ou l'utilisation de ptr ( là je met un ptr nu pour l'ex , mais je suppose qu'un shared_ptr , ou weak_ptr serait addapté, ) :

    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
     
     
       void Engine::load()
    {
     
           ...
           Point *position ( new Point ) ;
           Graphics graphics ( position , texture, etc ) ;
           Physics physics ( position , poid, etc 
           Collision collision( position , taille, type , etc ) ;
           ...
           _drawing.add ( _number , graphics );
           _physics.add ( _number , physics ) ;
           _ia.add ( _number, collision ) ;
     
    }
    est-ce bien cela ?

  9. #49
    Expert confirmé
    Homme Profil pro
    Étudiant
    Inscrit en
    Juin 2012
    Messages
    1 711
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Localisation : France

    Informations professionnelles :
    Activité : Étudiant

    Informations forums :
    Inscription : Juin 2012
    Messages : 1 711
    Points : 4 442
    Points
    4 442
    Par défaut
    Les ECS sont à là mode en ce moment. Donc oui ils sont pas mal utilisés.

    Citation Envoyé par mazertys17 Voir le message
    donc ça impliquerait un ascenseur non ? ou l'utilisation de ptr ( là je met un ptr nu pour l'ex , mais je suppose qu'un shared_ptr , ou weak_ptr serait addapté, ) :
    Pas forcément. C'est très bien les refs.
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    28
    29
    30
    31
    32
    33
    34
    35
    36
    37
    38
    39
    40
    41
    42
    43
    44
    45
    46
    47
    48
    49
    50
    51
    52
    53
    54
    55
    56
    57
    58
    59
    60
    61
    62
    63
    64
    65
    66
    67
    68
    69
    70
    71
    72
    73
    74
    75
    typedef int Object;
     
    struct Game {
        struct Data {
            std::unordered_map<Object, vec2> positions;
            std::unordered_map<Object, vec2> velocities;
            std::unordered_map<Object, vec2> accelerations;
            std::unordered_map<Object, mat3> transformations;
        };
     
        Data data;
     
        Physics p;
        RandomIA ia;
        Graphics g;
     
        Game(): p(data), ia(data), g(data) { }
     
        void update() {
            ia.update();
            p.update();
        }
     
        void draw() {
            g.update();
        }
    };
     
    struct Physics {
        Game::Data& data
        std::vector<Object> objs;
     
        explicit Physics(Game::Data& data): data(data) { }
     
        void update() { // prendre en compte le temps entre chaque call à update s'il n'est pas fixé (mais il est préférable qu'il le soit)
            for(Object o: objs) {
                velocities[o] += accelerations[o];
                positions[o] += velocities[o];
             }
         }
    };
     
    struct RandomIA {
        Game::Data& data
        std::vector<Object> objs;
     
        explicit RandomIA(Game::Data& data): data(data) { }
     
        void update() {
            for(Object o: objs) {
                accelerations[o] = vec2(rand() - RAND_MAX / 2, rand() - RAND_MAX / 2); // rand c'est mal -> std::random
             }
         }
    };
     
    struct Graphics {
        Game::Data& data
        Renderer& renderer; // permet d'abstraire la phase de rendering
        QuadTree<Object> objs; // ou n'importe quel conteneur adapté, on veut itérer
        // à travers les objets dans un certain ordre pour faciliter les calculs
     
        explicit Graphics(Game::Data& data): data(data) { }
     
        void update() {
            for(Object o: objs) { // on va dire que c'est une façon valide d'itérer sur un quadtree
                // culling relativement simple grace au quadtree
                renderer.draw(o); // ça peut être une simple préparation (mise à jour de vertex buffers par exemple)
             }
             renderer.present(); // maintenant que les données sont prêtes -> un ou plusieurs glDraw, mais le renderer "se démerde"
         }
    };
     
    // Renderer::draw utilisera "transformations" (pas forcément besoin de positions), mais même s'il en à besoin, ça reste utilisable.
    // Il faut bien sur faire gaffe dans un contexte multi threads si plusieurs systèmes travaillent sur les mêmes données.
    // Tu auras aussi probablement un graphe de scène qui te permettra de maintenir tes "transformations" à jour.
    C'est un exemple / une idée, et certainement pas la meilleure façon de faire.

    Dans le cas ou les données sont partagées, faut faire attention aux data races. (Ici accelerations est partagé entre 2 systèmes : RandomIA et Physics.

    Ya pas mal de décisions à faire (et la question de la performance ne doit pas être négligée : on ne peut pas changer facilement le design en cours de route dans le cas où "ça marche, mais pas assez vite"):
    • Une interface pour les systèmes ? Ce qui permettrait d'avoir un std::vector<std::unique_ptr<System>> dans Game pour réduire le couplage entre Game et les Systemes
    • Pattern observer ? Les systèmes s'abonnent à l'event "update" de Game, Game et les Systemes sont découplés
    • Comment les systèmes communiquent ? Références directes ? Par exemple Graphics possède une ref à Renderer : couplage fort.
    • Files de messages ? Ça réduit (voir supprime en fonction de comment c'est fait) le couplage entre systèmes. Au lieu d'un appel de fonction, on poste un msg, mais quelles garanties à-t-on ? Le msg va-t-il être traité assez rapidement ? Certains messages doivent être traités tout de suite et si on doit attendre l'itération suivante il peut être déjà trop tard.
    • Et surement un million d'autres choses.

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

    Informations professionnelles :
    Activité : aucun

    Informations forums :
    Inscription : Octobre 2004
    Messages : 11 612
    Points : 30 612
    Points
    30 612
    Par défaut
    Citation Envoyé par mintho carmo Voir le message
    Une bonne conception ne tient que jusqu'à ce qu'un développeur ne la respecte pas. Et c'est pas faute d'écrire du code clair, commenté, documenté, etc.
    Une bonne conception limitera surtout les situations dans lesquelles le développeur risquerait de ne pas la respecter...

    Si ta conception prévois d'assurer par construction qu'un élément "au dessus de la mélée" prend la responsabilité de la gestion de la durée de vie de tes objets, ton code reflétera cette construction, et ta garantie sera donnée "naturellement".
    Donc on est d'accord sur le fond : si on a la garantie d'avoir une bonne conception, il n'y a pas de problème. Malheureusement, cette garantie dépend de la garantie qu'aucun développeur ne fait d'erreur. Tu es pris en flagrant délits de bisounourisme ( ). Dans le monde réel, la seconde garantie n'est pas possible à avoir sur des gros projets à long termes, donc la première non plus.
    Non, je suis simplement habitué à lever le nez de mon code pour revoir ma conception dés qu'un problème à ce sujet montre le bout de son nez.

    Evidemment, cela implique deux choses aussi difficiles l'une que l'autre pour certaines personnes
    • d'être capable de repérer les symptômes liés à un problème de conception
    • d'être capable d'accepter le fait d'avoir fait une erreur et de se remettre en question

    Mais, quand on y arrive et qu'on arrive à lever le nez du code dés qu'un problème de conception apparait afin de trouver la méthode conceptuellement plus correcte de faire, il devient possible de rectifier le tir, même sur de très gros projets, en gâchant un minimum de code.

    Bien sur, sur les projets dont le développement a débuté plusieurs années ton intégration dans l'équipe, il y a des problèmes de conception qui nécessiteraient une refonte en profondeur d'une base de code énorme. Et c'est la raison pour laquelle il est -- vraiment -- préférable de revoir sa conception dés que le problème se pose. Mais, même lorsqu'un problème de conception nécessiterait de revoir une grosse partie de la base de code, il reste malgré tout toujours possible d'y aller "en douceur" pour améliorer les chose.

    Ce n'est pas vivre dans le monde des bisounours, c'est une constatation vécue

    Ne t'en fait pas pour moi niveau conception (bien que j'ai toujours à apprendre). Depuis des années que l'on discute ensemble sur ce forum et d'autres, tu devrais le savoir depuis le temps que j'ai une attention particulière sur la conception...
    Mon propos n'est pas d'utiliser shared_ptr à la place d'une bonne conception (si c'était le cas, je dirais d'utiliser des shared_ptr partout au lieu des weak_ptr et et je n'aurais pas approuvé l'importance d'avoir un propriétaire unique)
    Juste qu'une conception peut ne pas être respecter par erreur, et si c'est le cas, je veux des garanties que cette erreur sera détecter (de préférence avec un bel assert et un message d'insulte pour le développeur qui a fait l'erreur).
    Là, nous sommes d'accord
    Donc entre le fait tu as trop confiance dans les autres développeurs et que te trompes sur lock (qui est explicitement garantie dans la norme d'être atomique), je pense effectivement que tu te trompes sur ce coup là (pour une fois )
    Effectivement, sur ce coup, je me suis effectivement trompé
    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. #51
    Expert éminent sénior
    Avatar de koala01
    Homme Profil pro
    aucun
    Inscrit en
    Octobre 2004
    Messages
    11 612
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Âge : 52
    Localisation : Belgique

    Informations professionnelles :
    Activité : aucun

    Informations forums :
    Inscription : Octobre 2004
    Messages : 11 612
    Points : 30 612
    Points
    30 612
    Par défaut
    Citation Envoyé par Iradrille Voir le message
    Les ECS sont à là mode en ce moment. Donc oui ils sont pas mal utilisés.
    C'est même quasiment devenu la norme dans le domaine du développement de jeu à l'heure actuelle
    • Et surement un million d'autres choses.
    Mais surtout, le principal : l'approche ECS est totalement différente de l'approche orienté objet... Elle est plus proche de la modélisation d'une base de données relationnelle, avec un ensemble de tables "primaires" (une par composant de base) de tables "secondaires" (faisant des relations entre plusieurs tables primaires pour obtenir un "tout cohérent" correspondant à un ensemble d'entités spécifiques) et de "vues" (qui sont des "tables mises à jour", basées sur plusieurs tables, pour donner un aperçu des données qui soit spécifique à un besoin), le tout, agrémenté d'une bonne doses de requêtes, même si tu n'utilisera sans doute pas le langage SQL pour y arriver.

    Mais, je peux t'assurer que c'est ce qui te permettra le plus facilement de rajouter des éléments dans ton jeu
    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

  12. #52
    Membre habitué
    Homme Profil pro
    Étudiant
    Inscrit en
    Octobre 2014
    Messages
    521
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Âge : 36
    Localisation : France, Hérault (Languedoc Roussillon)

    Informations professionnelles :
    Activité : Étudiant

    Informations forums :
    Inscription : Octobre 2014
    Messages : 521
    Points : 136
    Points
    136
    Par défaut
    Merci pour les infos.

    Donc pas besoin de gérer le multi-tread ? et niveau perf, ce système est-il coûteux au final ? Et finalement, si ce n'est plus de la programmation orienté objet, est-ce toujours intéressant d'utiliser le c++ ?

    En tout cas, je reconnais que ca a l'air d'être une bonne facon de programmer, surtout pour pouvoir ajouter/modifier des fonctionnalités proprement.

    Je vais donc tenter de le mettre en place.

    Merci encore

    ps : si vous avez des liens de tutos/textes qui parlent de ce type de programmation, je suis preneur.

  13. #53
    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
    Le C++ est loin de se limiter à la programmation dite "orientée objet", c'est à dire les grosses hiérarchies de classes.
    Il reste un langage puissant et souple, ouvert à plusieurs approches.
    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

  14. #54
    Membre habitué
    Homme Profil pro
    Étudiant
    Inscrit en
    Octobre 2014
    Messages
    521
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Âge : 36
    Localisation : France, Hérault (Languedoc Roussillon)

    Informations professionnelles :
    Activité : Étudiant

    Informations forums :
    Inscription : Octobre 2014
    Messages : 521
    Points : 136
    Points
    136
    Par défaut
    ok, merci.

    Donc j'ai l'impression que j'ai tout a gagner à me lancer dans la programmation ECS, ( si j'ai bien compris ).

  15. #55
    Membre éclairé

    Profil pro
    Inscrit en
    Décembre 2013
    Messages
    393
    Détails du profil
    Informations personnelles :
    Localisation : France

    Informations forums :
    Inscription : Décembre 2013
    Messages : 393
    Points : 685
    Points
    685
    Par défaut
    Citation Envoyé par koala01 Voir le message
    aussi difficiles l'une que l'autre pour certaines personnes
    (...)
    nécessiterait de revoir une grosse partie de la base de code, il reste malgré tout toujours possible d'y aller "en douceur" pour améliorer les chose.
    Ce que je trouve important de prendre en compte, c'est que "certaines personnes" ne vont pas forcement respecter le design et que le design peut ne pas être correct (même temporairement)
    Donc, vouloir avoir la garantie que les erreurs de programmation soient correctement détectées, ce n'est pas une aberration. (je ne pense être dans la programmation défensive en voulant ces garanties).

    D'autant plus que finalement, (puisque l'on est d'accord sur l'importance de la conception et d'avoir un responsable unique - ce qu'apporte un shared_ptr seul + des weak_ptr), le choix de ne pas utiliser cette approche avec shred_ptr + des weak_ptr est justifie uniquement pour des raisons de performances (ne pas utiliser des opérations atomiques).
    Sachant que l'on perd des garanties et que le coût au final n'est pas forcement critique (les opérations atomiques ont lieu que lors de la création et destruction des shared/weak), cela semble etre pour moi de la premature optimization au final (perte de garantie pour un gain de performances non prouvé).


    Pour revenir sur la conception (et un peu sur la question du PO), revenons sur un cas concret.
    Imagine dans un jeu que tu as un "RessourceProvider" et un ECS. Le premier fournit une ressource, le second l'utilise.
    Le RessourceProvider fonctionne avec un système de cache : l'utilisateur demande une ressource, le provider vérifie dans le cache si la ressource est chargée et la charge si ce n'est pas le cas. Si le cache est trop plein, le provider supprime les ressources qui n'ont pas été utilisées depuis longtemps (en fait peut importe la stratégie. le plus important est que seul le provider peut être responsable de la destruction des ressources, puisque lui seul sait quelles ressources sont utilisées et quand).
    L'utilisateur (par exemple un ECS, puisque l'on en parle. Mais peu importe également, cela pourrait être un système de scenegraph OpenGL en backend d'un ECS. Le plus important est qu'il ne peut pas avoir le contrôle de la durée de vie de la ressource utilisée)

    Je pense que cette problème est possible.

    On doit donc avoir les garanties suivantes : le provider décide lorsqu'une ressource doit être détruite, mais celle-ci doit l’être effectivement que si elle n'est pas utilisée a ce moment. Si elle l'est, elle doit être détruite automatiquement dès que possible. L'utilisateur doit pouvoir utiliser cette ressource sans danger (en particulier dangling pointer) et si cette ressource n'est pas disponible, il doit la demander au provider.

    Il est bien sur possible de faire cela sans shared_ptr/weak_ptr, mais cela me semble être une approche simple a mettre en place, safe et avec des performances acceptables - tant que l'on a pas prouvé que l'utilisation d'un shared_ptr est critique en termes de performances.
    Un système a base de unique_ptr et/ou reference_wrapper nécessitera d’implémenter en plus un système de communication entre le provider et les utilisateur (pour que le provider puisse demander aux utilisateurs s'il peut effacer une ressource et/ou que les utilisateurs puissent faire un lock de la ressource)

    Bref, pour moi, 1 shared_ptr + des weak_ptr est une solution simple, safe et respectueuse de la conception, qui ne doit pas rejetée du fait que ce soit un shared_ptr.

+ Répondre à la discussion
Cette discussion est résolue.
Page 3 sur 3 PremièrePremière 123

Discussions similaires

  1. Héritage et tableau d'objets
    Par Mindiell dans le forum C++
    Réponses: 17
    Dernier message: 08/01/2009, 07h24
  2. [POO] Decoupage de chaine et objet
    Par bobspike dans le forum Général JavaScript
    Réponses: 3
    Dernier message: 27/06/2008, 15h32
  3. Problème d'héritage css (classe et objet)
    Par Askle dans le forum Mise en page CSS
    Réponses: 2
    Dernier message: 03/06/2007, 20h11
  4. Mal a la tete avec liste chainée d'objet
    Par Raton dans le forum C++
    Réponses: 23
    Dernier message: 03/08/2005, 22h13
  5. [Java] Héritage et flux d'Objet
    Par guejo dans le forum CORBA
    Réponses: 2
    Dernier message: 22/02/2005, 11h14

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