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

Langage C++ Discussion :

Problème avec le design pattern visitor


Sujet :

Langage C++

  1. #1
    Nouveau membre du Club
    Homme Profil pro
    Étudiant
    Inscrit en
    Décembre 2010
    Messages
    33
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Âge : 34
    Localisation : France, Hérault (Languedoc Roussillon)

    Informations professionnelles :
    Activité : Étudiant

    Informations forums :
    Inscription : Décembre 2010
    Messages : 33
    Points : 35
    Points
    35
    Par défaut Problème avec le design pattern visitor
    Bonjour,

    Je suis en train de coder un petit moteur 2D, et, étant débutant, je me heurte à de nombreux problèmes de conceptions dont un que je n'arrive pas à résoudre seul. En effet, j'aimerais mettre en œuvre un système de gestion de collisions capable de faire interagir différents types de collisions entres eux (Box, Point, Circle...).

    Afin de rendre opaque les différents algorithmes d'interaction des collisions et de rendre générique leur utilisation, j'étais parti sur l'idée de créer une classe abstraite ACollision, de laquelle je ferais dériver une classe fille par type de collision à gérer. Voici un exemple édulcoré de code pour donner une idée plus précise de ce que j'imagine :

    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
    class ACollision
    {
    public :
    	virtual 	~ACollision( void ) {};
    	virtual	void	check_collision( const Box& box ) = 0;
    	virtual	void	check_collision( const Circle& circle ) = 0;
    	virtual	void	check_collision( const Point& point ) = 0;
    };
     
    class Box : public ACollision
    {
    public :
    	virtual	void	check_collision( const Box& box )
    	{
    		std::cout << "Box/Box collision" << std::endl;
    	}
    	virtual	void	check_collision( const Circle& circle )
    	{
    		std::cout << "Box/Circle collision" << std::endl;
    	}
    	virtual	void	check_collision( const Point& point )
    	{
    		std::cout << "Box/Point collision" << std::endl;
    	}
    };
     
    class Circle : public ACollision
    {
    public :
    	virtual	void	check_collision( const Box& box )
    	{
    		std::cout << "Circle/Box collision" << std::endl;
    	}
    	virtual	void	check_collision( const Circle& circle )
    	{
    		std::cout << "Circle/Circle collison" << std::endl;
    	}
    	virtual	void	check_collision( const Point& point )
    	{
    		std::cout << "Circle/Point collision" << std::endl;
    	}
    };
     
    class Point : public ACollision
    {
    public :
    	virtual	void	check_collision( const Box& box )
    	{
    		std::cout << "Point/Box collision" << std::endl;
    	}
    	virtual	void	check_collision( const Circle& circle )
    	{
    		std::cout << "Point/Circle collison" << std::endl;
    	}
    	virtual	void	check_collision( const Point& point )
    	{
    		std::cout << "Point/Point collision" << std::endl;
    	}
    };
    J'aurais donc voulu gérer mes collisions par polymorphisme et laisser les différentes surcharges de la fonction check_collision s'occuper d'utiliser le bon algorithme pour moi. Seulement voilà, sur un pointeur ACollision, impossible d’appeler les méthodes check_collision puisqu'elles ne sont pas encore connues. J'aurais pu faire des dynamic_cast pour résoudre le problème, mais j'ai lu partout que c'était à éviter car vraiment pas propre.

    J'ai donc demandé conseil sur le chat du site, et on m'a orienté vers le design pattern visitor, qui semble en effet tout indiqué dans mon cas. Seulement voilà, si je l'implémente dans sa forme classique, je ne résous pas vraiment mon problème. En effet, les implémentations que j'ai pu voir du pattern visitor permettent bien de déclencher une action spécifique à un type de classe donné, mais pas de faire interagir deux classes entre elles. Voilà ce que j'ai tenté :

    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
    76
    77
    78
    79
    80
    81
    82
    83
    84
    85
    86
    87
    88
    89
    90
    91
    92
    93
    94
    95
    96
    97
    98
    99
    100
    101
    102
    103
    104
    105
    106
    107
    108
    109
    110
    111
    112
    113
    114
    115
    116
    117
    118
    119
    120
    121
    122
    123
    124
    125
    126
    127
    128
    129
    130
    131
    132
    133
    134
    #include <iostream>
    #include <list>
     
    class Visitor;
    class Box;
    class Circle;
    class Point;
     
    class Visitor
    {
      public:
        virtual void visit( Box& box ) = 0;
        virtual void visit( Circle& circle ) = 0;
        virtual void visit( Point& point ) = 0;
    };
     
    class ACollision
    {
    public :
    	virtual 	~ACollision( void ) {};
    	virtual	void	accept( Visitor& visitor ) = 0;
    };
     
    class Box : public ACollision
    {
    public :
    	virtual	void	accept( Visitor& visitor )
    	{
    		return ( visitor.visit( *this ) );
    	}
    	virtual	void	check_collision( const Box& box )
    	{
    		std::cout << "Box/Box collision" << std::endl;
    	}
    	virtual	void	check_collision( const Circle& circle )
    	{
    		std::cout << "Box/Circle collision" << std::endl;
    	}
    	virtual	void	check_collision( const Point& point )
    	{
    		std::cout << "Box/Point collision" << std::endl;
    	}
    };
     
    class Circle : public ACollision
    {
    public :
    	virtual	void	accept( Visitor& visitor )
    	{
    		return ( visitor.visit( *this ) );
    	}
    	virtual	void	check_collision( const Box& box )
    	{
    		std::cout << "Circle/Box collision" << std::endl;
    	}
    	virtual	void	check_collision( const Circle& circle )
    	{
    		std::cout << "Circle/Circle collison" << std::endl;
    	}
    	virtual	void	check_collision( const Point& point )
    	{
    		std::cout << "Circle/Point collision" << std::endl;
    	}
    };
     
    class Point : public ACollision
    {
    public :
    	virtual	void	accept( Visitor& visitor )
    	{
    		return ( visitor.visit( *this ) );
    	}
    	virtual	void	check_collision( const Box& box )
    	{
    		std::cout << "Point/Box collision" << std::endl;
    	}
    	virtual	void	check_collision( const Circle& circle )
    	{
    		std::cout << "Point/Circle collison" << std::endl;
    	}
    	virtual	void	check_collision( const Point& point )
    	{
    		std::cout << "Point/Point collision" << std::endl;
    	}
    };
     
    class Collision_visitor : public Visitor
    {
    public :
    	Collision_visitor(const ACollision& object) :
    		object( object )
    	{
    	}
        virtual void visit( Box& box )
    	{
    		object.check_collision( box ); //Problème : l'une des deux classes à comparer n'est pas de type concret, je n'ai donc pas moyen de faire appeler la bonne méthode check_collision!
    	}
        virtual void visit( Circle& circle )
    	{
    		object.check_collision( circle ); //Problème : l'une des deux classes à comparer n'est pas de type concret, je n'ai donc pas moyen de faire appeler la bonne méthode check_collision!
    	}
    	virtual void visit( Point& point )
    	{
    		object.check_collision( point ); //Problème : l'une des deux classes à comparer n'est pas de type concret, je n'ai donc pas moyen de faire appeler la bonne méthode check_collision!
    	}
    private :
    	const ACollision&	object;
    };
     
    int main( void )
    {
    	std::list< ACollision* >	objects_list;
     
    	objects_list.push_back( new ( Box ) );
    	objects_list.push_back( new ( Circle ) );
    	objects_list.push_back( new ( Point ) );
     
    	std::list< ACollision* >::iterator	objects_it1 = objects_list.begin();
    	std::list< ACollision* >::iterator	objects_end = objects_list.end();
     
    	for ( ; objects_it1 != objects_end ; ++objects_it1)
    	{
    		std::list< ACollision* >::iterator	objects_it2 = objects_list.begin();
     
    		for ( ; objects_it2 != objects_end ; ++objects_it2 )
    		{
    			Collision_visitor		collision_visitor( *( *objects_it2 ) );
     
    			( *objects_it1 )->accept( collision_visitor );
    		}
    	}
     
    	return ( 0 );
    }
    J'ai beau retourner le problème dans tous les sens, j'ai toujours l'une des deux classes dont le type m'est inconnu, ce qui m'empêche de pouvoir appeller la bonne méthode. Sur l'exemple ci dessus, dans la classe Collision_visitor, je me retrouve avec une référence sur un ACollision, et une classe fille dont le type est donc connu. Seulement, impossible de faire appeler la bonne méthode check_collision via la référence sur ACollision, car cette dernière ne connais pas encore cette méthode. A l'inverse, impossible d'envoyer la classe ACollision à la classe de type connu, car les méthodes check_collisions sont censée traiter un type précis, hors ACollision n'est pas une collision traitable en tant que telle.

    J'ai tenté de trafiquer le code précédent, et j'ai trouvé une solution qui fonctionne parfaitement. Seulement voilà, pour résoudre mon problème, j'ai dû déclarer des méthodes dans la classe mère qui reçoivent des types de classe fille. Sans être un crack en conception, il me semble que le fait d'avoir une classe mère au courant des classes filles est relativement crado. Voilà ce que ça donne :

    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
    76
    77
    78
    79
    80
    81
    82
    83
    84
    85
    86
    87
    88
    89
    90
    91
    92
    93
    94
    95
    96
    97
    98
    99
    100
    #include <iostream>
    #include <list>
     
    class Box;
    class Circle;
    class Point;
     
    class ACollision
    {
    public :
    	virtual 	~ACollision( void ) {};
    	virtual	void	check_collision( ACollision& object ) = 0;
    	virtual	void	check_collision( const Box& box ) = 0;
    	virtual	void	check_collision( const Circle& circle ) = 0;
    	virtual	void	check_collision( const Point& point ) = 0;
    };
     
    class Box : public ACollision
    {
    public :
    	virtual	void	check_collision( ACollision& object )
    	{
    		return ( object.check_collision( *this ) );
    	}
    	virtual	void	check_collision( const Box& box )
    	{
    		std::cout << "Box/Box collision" << std::endl;
    	}
    	virtual	void	check_collision( const Circle& circle )
    	{
    		std::cout << "Box/Circle collision" << std::endl;
    	}
    	virtual	void	check_collision( const Point& point )
    	{
    		std::cout << "Box/Point collision" << std::endl;
    	}
    };
     
    class Circle : public ACollision
    {
    public :
    	virtual	void	check_collision( ACollision& object )
    	{
    		return ( object.check_collision( *this ) );
    	}
    	virtual	void	check_collision( const Box& box )
    	{
    		std::cout << "Circle/Box collision" << std::endl;
    	}
    	virtual	void	check_collision( const Circle& circle )
    	{
    		std::cout << "Circle/Circle collison" << std::endl;
    	}
    	virtual	void	check_collision( const Point& point )
    	{
    		std::cout << "Circle/Point collision" << std::endl;
    	}
    };
     
    class Point : public ACollision
    {
    public :
    	virtual	void	check_collision( ACollision& object )
    	{
    		return ( object.check_collision( *this ) );
    	}
    	virtual	void	check_collision( const Box& box )
    	{
    		std::cout << "Point/Box collision" << std::endl;
    	}
    	virtual	void	check_collision( const Circle& circle )
    	{
    		std::cout << "Point/Circle collison" << std::endl;
    	}
    	virtual	void	check_collision( const Point& point )
    	{
    		std::cout << "Point/Point collision" << std::endl;
    	}
    };
     
    int main( void )
    {
    	std::list< ACollision* >	objects_list;
     
    	objects_list.push_back( new ( Box ) );
    	objects_list.push_back( new ( Circle ) );
    	objects_list.push_back( new ( Point ) );
     
    	std::list< ACollision* >::iterator	objects_it1 = objects_list.begin();
    	std::list< ACollision* >::iterator	objects_end = objects_list.end();
     
    	for ( ; objects_it1 != objects_end ; ++objects_it1)
    	{
    		std::list< ACollision* >::iterator	objects_it2 = objects_list.begin();
     
    		for ( ; objects_it2 != objects_end ; ++objects_it2 )
    			( *objects_it1 )->check_collision( *( *objects_it2 ) );
    	}
    	return ( 0 );
    }
    Ce code affiche :
    Box/Box collision
    Circle/Box collision
    Point/Box collision
    Box/Circle collision
    Circle/Circle collison
    Point/Circle collison
    Box/Point collision
    Circle/Point collision
    Point/Point collision
    Ce qui est tout à fait valide. C'est exactement le comportement que je recherchais.

    J'en arrive donc à ma question : Comment pourrais-je implémenter ce mécanisme de manière propre?

    En vous remerciant d'avance.

  2. #2
    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,

    En fait, tes types box, circle, point et consort sont... tout sauf de collisions

    Par contre, une collision a, d'office, lieu entre deux objets (quel que soit leur type), et les objets en question font partie du "contexte dans lequel la collision survient"

    L'idéal est donc de créer une hiérarchie de classes "collision" qui représentera les différentes possibilités.

    Le problème, c'est que tu vas avoir 2^N possibilités de collisions, où N représente le nombre de types différents que tu veux faire entrer en collision, ce qui ne va pas faciliter la mise au point de ton pattern visiteur, du moins sans passer par la programmation générique

    Par contre, avec la programmation générique, on peut envisager quelques trucs sympa, car, en gros, lorsque deux objets entre en collision, soit ce sont des objets de type identique, soit ce sont des objets de type différent et l'on peut toujours considérer qu'il y a un objet qui "provoque" la collision (qui a une part active dans la collision) et l'autre qui la "subit".

    Tu pourrais donc avoir une hiérarchie de collision proche de
    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
    class AbstractCollision
    {
        public:
            virtual ~AbstractCollision();
            virtual void accept(Visiteur /* const */ &) =0;
     
    };
    template <typename T>
    class TwoSameObjectCollision : public AbstractCollision
    {
        public:
            typedef T * activeObjectPtr;
            typedef T * passiveObjectPtr;
            TwoSameObjectCollision(activeObjectPtr active, passiveObjectPtr passive): active_(active), passive_(passive){}
            accept(Visiteur /* const */ & v)
            {
                v.visit(*this);
            }
            activeObjectPtr activeObject() {return active_;}
            passiveObjectPtr passiveObject(){return passive_;}
        private:
            activeObjectPtr active_;
            passiveObjectPtr passive_;
    };
     
    template <typename T, typename U>
    class TwoDifferentObjectCollision : public AbstractCollision
    {
        public:
            typedef T * activeObjectPtr;
            typedef U * passiveObjectPtr;
            TwoDifferentObjectCollision (activeObjectPtr active, passiveObjectPtr passive): active_(active), passive_(passive){}
            accept(Visiteur /* const */ & v)
            {
                v.visit(*this);
            }
            activeObjectPtr activeObject() {return active_;}
            passiveObjectPtr passiveObject(){return passive_;}
        private:
            activeObjectPtr active_;
            passiveObjectPtr passive_;
    };
    Ton visiteur pourrait alors ressembler à quelque chose comme
    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
    class Visitor
    {
        public:
            template <typename T>
            visit(TwoSameObjectCollision<T> /* const */ &)
            {
                 //someStuff
            }
            template <typename T, typename U>
            visit(TwoSameObjectCollision<T,U> /* const */ &)
            {
                 //someStuff
            }
     
    };
    Il restera le problème qu'il faut sans doute pouvoir spécialiser les comportement en fonction des types d'objet qui entrent en collision...

    Pour ce faire, rien ne vaut (sans doute) les trait de politique

    Tu pourrait en effet creer un trait proche de
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    template< typename T, typename U>
    struct CollisionTrait;
    que tu spécialiserait pour chaque type de collision envisagé, donc, sous une forme proche de
    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
    /* selon les situation,  les deux types sont clairement définis: */
    // un point avec un point
    template <>
    struct CollisionTrait<Point, Point>
    {
        void operator()(Point * first, Point * second)
        {
            //ce qui doit etre fait
        }
    }
    /* ou bien, il n'y a qu'un seul type qui est sur */
    // un "box" avec n'importe quoi
    template <typename U>
    struct CollisionTrait<Box, U>
    {
        void operator()(Box* first, Box* second)
        {
            //ce qui doit etre fait
        }
     
    };
    et le code de ton visiteur pourrait alors resembler à
    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
    class Visitor
    {
        public:
            template <typename T>
            visit(TwoSameObjectCollision<T> /* const */ & c)
            {
                 CollisionTrait<TwoSameObjectCollision<T>::activeObjectPtr ,
                                TwoSameObjectCollision<T>::passiveObjectPtr >() 
                  (c.activeObject(), c.passiveObject());
            }
            template <typename T, typename U>
            visit(TwoDifferentObjectCollision <T,U> /* const */ & c)
            {
                 CollisionTrait<TwoDifferentObjectCollision <T,U>::activeObjectPtr ,
                                TwoDifferentObjectCollision <T,U>::passiveObjectPtr >() 
                  (c.activeObject(), c.passiveObject());
     
            }
     
    };
    Pas mal, hein?

    Chaque fois que tu voudras rajouter un type d'objet suceptible de rentrer en collision avec un autre, il te "suffira" de créer les spécialisation de CollisionTrait qui vont bien
    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

  3. #3
    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
    Bonjour,

    Seulement voilà, sur un pointeur ACollision, impossible d’appeler les méthodes check_collision puisqu'elles ne sont pas encore connues.
    Je n'ai pas compris ce passage ?!
    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.

  4. #4
    Membre régulier
    Profil pro
    Inscrit en
    Avril 2009
    Messages
    199
    Détails du profil
    Informations personnelles :
    Localisation : France

    Informations forums :
    Inscription : Avril 2009
    Messages : 199
    Points : 106
    Points
    106
    Par défaut
    @koala01: Dans ton implémentation finale du visiteur (copier/coller ci-dessous), tu ne te serais pas tromper ligne 12 en passant un TwoSameObjectCollision au lieu d'un TwoDifferentObjectCollision (même si je pense qu'un pense qu'un c'est un oublie juste, puisque les paramètres template sont bien ceux de TwoDifferentObjectCollision )

    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
    class Visitor
    {
        public:
            template <typename T>
            visit(TwoSameObjectCollision<T> /* const */ & c)
            {
                 CollisionTrait<TwoSameObjectCollision<T>::activeObjectPtr ,
                                TwoSameObjectCollision<T>::passiveObjectPtr >() 
                  (c.activeObject(), c.passiveObject());
            }
            template <typename T, typename U>
            visit(TwoSameObjectCollision<T,U> /* const */ & c)
            {
                 CollisionTrait<TwoDifferentObjectCollision <T,U>::activeObjectPtr ,
                                TwoDifferentObjectCollision <T,U>::passiveObjectPtr >() 
                  (c.activeObject(), c.passiveObject());
     
            }
     
    };
    Si c'est une bêtise que je dis, veuillez m'en excuser

  5. #5
    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
    Citation Envoyé par victor_gasgas Voir le message
    @koala01: Dans ton implémentation finale du visiteur (copier/coller ci-dessous), tu ne te serais pas tromper ligne 12 en passant un TwoSameObjectCollision au lieu d'un TwoDifferentObjectCollision (même si je pense qu'un pense qu'un c'est un oublie juste, puisque les paramètres template sont bien ceux de TwoDifferentObjectCollision )

    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
    class Visitor
    {
        public:
            template <typename T>
            visit(TwoSameObjectCollision<T> /* const */ & c)
            {
                 CollisionTrait<TwoSameObjectCollision<T>::activeObjectPtr ,
                                TwoSameObjectCollision<T>::passiveObjectPtr >() 
                  (c.activeObject(), c.passiveObject());
            }
            template <typename T, typename U>
            visit(TwoSameObjectCollision<T,U> /* const */ & c)
            {
                 CollisionTrait<TwoDifferentObjectCollision <T,U>::activeObjectPtr ,
                                TwoDifferentObjectCollision <T,U>::passiveObjectPtr >() 
                  (c.activeObject(), c.passiveObject());
     
            }
     
    };
    Si c'est une bêtise que je dis, veuillez m'en excuser
    Non, ce n'est pas une bêtise du tout, c'est bel et bien un copier / coller foireux de ma part

    Merci de me l'avoir signalée, je corrige le code
    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. #6
    Nouveau membre du Club
    Homme Profil pro
    Étudiant
    Inscrit en
    Décembre 2010
    Messages
    33
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Âge : 34
    Localisation : France, Hérault (Languedoc Roussillon)

    Informations professionnelles :
    Activité : Étudiant

    Informations forums :
    Inscription : Décembre 2010
    Messages : 33
    Points : 35
    Points
    35
    Par défaut
    Alors pour commencer, je tiens à m'excuser pour le retard de ma réponse. Ce topic m'était complètement sortit de la tête, et devant la qualité de la réponse de koala01, je me sens minable d'avoir mis si longtemps à revenir ici. Un grand merci donc pour cette réponse qui, je dois te l'avouer, m'a donné du fil à retordre car j'avais jusqu'ici consciencieusement évité de toucher aux templates dont je hais la syntaxe. Du coup je m'y suis attelé, et j'ai appris plein de choses super intéressantes, comme les spécialisations, les classes de traits et de politique... Ce qui fût très enrichissant pour moi donc.

    Par contre, pour être franc, ça n'a pas vraiment résolu mon soucis, car si j'ai bien compris le code que tu as posté, le problème initial est évité. En effet, tu me proposes des classes auxquelles je suis censé envoyer mes deux collisions à traiter ( TwoSameObjectCollision et TwoDifferentObjectCollision ). Seulement mon problème, c'est que je ne connais pas les types réels de mes collisions, puisqu'elle sont stockées par polymorphisme, hors tes classes requièrent de connaître le type exact des collisions à traiter pour être créées.

    A force de recherches, j'ai fini par trouver que ce que je cherchais à faire se nomme le double dispatching, et que le c++ n'est pas vraiment prévu pour ça. Je suis tombé sur plusieurs sources ( dont cet excellent article : http://blog.emmanueldeloget.com/inde...comme-visiteur ) expliquant qu'une des seules manières à peu près correcte de le faire en c++ était de procéder comme je l'avais fait en premier lieu, c'est à dire à peu près comme ceci :

    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
    76
    77
    78
    79
    80
    81
    82
    83
    84
    85
    86
    87
    88
    89
    90
    91
    92
    93
    94
    95
    96
    97
    98
    99
    100
    #include <iostream>
    #include <list>
     
    class Box;
    class Circle;
    class Point;
     
    class ACollision
    {
    public :
    	virtual 	~ACollision( void ) {};
    	virtual	void	check_collision( ACollision& object ) = 0;
    	virtual	void	check_collision( const Box& box ) = 0;
    	virtual	void	check_collision( const Circle& circle ) = 0;
    	virtual	void	check_collision( const Point& point ) = 0;
    };
     
    class Box : public ACollision
    {
    public :
    	virtual	void	check_collision( ACollision& object )
    	{
    		return ( object.check_collision( *this ) );
    	}
    	virtual	void	check_collision( const Box& box )
    	{
    		std::cout << "Box/Box collision" << std::endl;
    	}
    	virtual	void	check_collision( const Circle& circle )
    	{
    		std::cout << "Box/Circle collision" << std::endl;
    	}
    	virtual	void	check_collision( const Point& point )
    	{
    		std::cout << "Box/Point collision" << std::endl;
    	}
    };
     
    class Circle : public ACollision
    {
    public :
    	virtual	void	check_collision( ACollision& object )
    	{
    		return ( object.check_collision( *this ) );
    	}
    	virtual	void	check_collision( const Box& box )
    	{
    		std::cout << "Circle/Box collision" << std::endl;
    	}
    	virtual	void	check_collision( const Circle& circle )
    	{
    		std::cout << "Circle/Circle collison" << std::endl;
    	}
    	virtual	void	check_collision( const Point& point )
    	{
    		std::cout << "Circle/Point collision" << std::endl;
    	}
    };
     
    class Point : public ACollision
    {
    public :
    	virtual	void	check_collision( ACollision& object )
    	{
    		return ( object.check_collision( *this ) );
    	}
    	virtual	void	check_collision( const Box& box )
    	{
    		std::cout << "Point/Box collision" << std::endl;
    	}
    	virtual	void	check_collision( const Circle& circle )
    	{
    		std::cout << "Point/Circle collison" << std::endl;
    	}
    	virtual	void	check_collision( const Point& point )
    	{
    		std::cout << "Point/Point collision" << std::endl;
    	}
    };
     
    int main( void )
    {
    	std::list< ACollision* >	objects_list;
     
    	objects_list.push_back( new ( Box ) );
    	objects_list.push_back( new ( Circle ) );
    	objects_list.push_back( new ( Point ) );
     
    	std::list< ACollision* >::iterator	objects_it1 = objects_list.begin();
    	std::list< ACollision* >::iterator	objects_end = objects_list.end();
     
    	for ( ; objects_it1 != objects_end ; ++objects_it1)
    	{
    		std::list< ACollision* >::iterator	objects_it2 = objects_list.begin();
     
    		for ( ; objects_it2 != objects_end ; ++objects_it2 )
    			( *objects_it1 )->check_collision( *( *objects_it2 ) );
    	}
    	return ( 0 );
    }
    Et en utilisant les connaissances acquises grâce à la réponse de koala01, j'ai amélioré cet exemple ce qui donne l'implémentation suivante :

    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
    76
    77
    78
    79
    80
    81
    82
    83
    84
    85
    86
    87
    88
    89
    90
    91
    92
    93
    94
    95
    96
    97
    98
    99
    100
    101
    102
    103
    104
    105
    106
    107
    108
    109
    110
    111
    112
    113
    114
    115
    116
    117
    118
    119
    120
    121
    122
    123
    124
    125
    126
    127
    128
    129
    130
    131
    132
    133
    134
    135
    #include <iostream>
    #include <list>
     
    class Abstract_shape
    {
    public :
    	virtual ~Abstract_shape( void ) {};
    };
     
    class Box : public Abstract_shape {};
     
    class Circle : public Abstract_shape {};
     
    class Point : public Abstract_shape {};
     
    class Abstract_collision
    {
    public :
    	virtual 	~Abstract_collision( void ) {};
    	virtual	void	check_collision( Abstract_collision& object ) = 0;
    	virtual	void	check_collision( const Box& box ) {};
    	virtual	void	check_collision( const Circle& circle ) {};
    	virtual	void	check_collision( const Point& point ) {};
    };
     
    template < typename T >
    class Collision : public Abstract_collision
    {
    public :
    			Collision( T& collision_model ) : collision_model( collision_model ) {}
    	virtual	void	check_collision( Abstract_collision& object ) {}
     
    private :
    	T&	collision_model;
    };
     
    template < >
    class Collision< Box > : public Abstract_collision
    {
    public :
    			Collision( Box& collision_model ) : collision_model( collision_model ) {}
    	virtual	void	check_collision( Abstract_collision& object )
    	{
    		object.check_collision( collision_model );
    	}
    	virtual	void	check_collision( const Box& box )
    	{
    		std::cout << "Box/Box collision" << std::endl;
    	}
    	virtual	void	check_collision( const Circle& circle )
    	{
    		std::cout << "Circle/Box collision" << std::endl;
    	}
    	virtual	void	check_collision( const Point& point )
    	{
    		std::cout << "Point/Box collision" << std::endl;
    	}
     
    private :
    	Box&	collision_model;
    };
     
    template < >
    class Collision< Circle > : public Abstract_collision
    {
    public :
    			Collision( Circle& collision_model ) : collision_model( collision_model ) {}
    	virtual	void	check_collision( Abstract_collision& object )
    	{
    		object.check_collision( collision_model );
    	}
    	virtual	void	check_collision( const Box& box )
    	{
    		std::cout << "Box/Circle collision" << std::endl;
    	}
    	virtual	void	check_collision( const Circle& circle )
    	{
    		std::cout << "Circle/Circle collision" << std::endl;
    	}
    	virtual	void	check_collision( const Point& point )
    	{
    		std::cout << "Point/Circle collision" << std::endl;
    	}
     
    private :
    	Circle&	collision_model;
    };
     
    template < >
    class Collision< Point > : public Abstract_collision
    {
    public :
    			Collision( Point& collision_model ) : collision_model( collision_model ) {}
    	virtual	void	check_collision( Abstract_collision& object )
    	{
    		object.check_collision( collision_model );
    	}
    	virtual	void	check_collision( const Box& box )
    	{
    		std::cout << "Box/Point collision" << std::endl;
    	}
    	virtual	void	check_collision( const Circle& circle )
    	{
    		std::cout << "Circle/Point collision" << std::endl;
    	}
    	virtual	void	check_collision( const Point& point )
    	{
    		std::cout << "Point/Point collision" << std::endl;
    	}
     
    private :
    	Point&	collision_model;
    };
     
    int main( void )
    {
    	std::list< Abstract_collision* >	objects_list;
     
    	objects_list.push_back( new ( Collision< Box > )( *( new ( Box ) ) ) );
    	objects_list.push_back( new ( Collision< Circle > )( *( new ( Circle ) ) ) );
    	objects_list.push_back( new ( Collision< Point > )( *( new ( Point ) ) ) );
    	objects_list.push_back( new ( Collision< int > )( *( new ( int ) ) ) );
     
    	std::list< Abstract_collision* >::iterator	objects_it1 = objects_list.begin();
    	std::list< Abstract_collision* >::iterator	objects_end = objects_list.end();
     
    	for ( ; objects_it1 != objects_end ; ++objects_it1)
    	{
    		std::list< Abstract_collision* >::iterator	objects_it2 = objects_list.begin();
     
    		for ( ; objects_it2 != objects_end ; ++objects_it2 )
    			( *objects_it1 )->check_collision( **objects_it2 );
    	}
    	return ( 0 );
    }
    J'imagine que ça n'est pas parfait du tout, mais je trouve ça simple et efficace, alors je compte partir là dessus. Ceci dit, je serais ravi d'avoir des retours sur ce code afin d'être sûr de ne pas avoir fait de grosses bêtises, ou plus simplement pour l'améliorer.

    Bonjour,

    Citation:
    Seulement voilà, sur un pointeur ACollision, impossible d’appeler les méthodes check_collision puisqu'elles ne sont pas encore connues.
    Je n'ai pas compris ce passage ?!
    Ah oui pardon, c'est parce que je me suis trompé. Le code qui précède cette phrase dans mon premier post n'est pas le bon, voilà ce qu'il aurait dû être :

    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
    class ACollision
    {
    public :
    	virtual 	~ACollision( void ) {};
    };
     
    class Box : public ACollision
    {
    public :
    	void	check_collision( const Box& box )
    	{
    		std::cout << "Box/Box collision" << std::endl;
    	}
    	void	check_collision( const Circle& circle )
    	{
    		std::cout << "Box/Circle collision" << std::endl;
    	}
    	void	check_collision( const Point& point )
    	{
    		std::cout << "Box/Point collision" << std::endl;
    	}
    };
     
    class Circle : public ACollision
    {
    public :
    	void	check_collision( const Box& box )
    	{
    		std::cout << "Circle/Box collision" << std::endl;
    	}
    	void	check_collision( const Circle& circle )
    	{
    		std::cout << "Circle/Circle collison" << std::endl;
    	}
    	void	check_collision( const Point& point )
    	{
    		std::cout << "Circle/Point collision" << std::endl;
    	}
    };
     
    class Point : public ACollision
    {
    public :
    	void	check_collision( const Box& box )
    	{
    		std::cout << "Point/Box collision" << std::endl;
    	}
    	void	check_collision( const Circle& circle )
    	{
    		std::cout << "Point/Circle collison" << std::endl;
    	}
    	void	check_collision( const Point& point )
    	{
    		std::cout << "Point/Point collision" << std::endl;
    	}
    };
    Et dans ce cas ci donc, impossible d’appeler les fonctions check_collision à partir d'un pointeur sur ACollision. C'est ce que je voulais dire.

+ Répondre à la discussion
Cette discussion est résolue.

Discussions similaires

  1. Problème d'accessibilité avec le design patterns MVC
    Par radical_bombtracks dans le forum JSF
    Réponses: 5
    Dernier message: 24/07/2007, 13h15
  2. Réponses: 1
    Dernier message: 24/01/2007, 16h00
  3. [2005] Problème avec le designer
    Par vandamme dans le forum Visual Studio
    Réponses: 1
    Dernier message: 07/01/2007, 01h14
  4. [Curiosité] design pattern visitor et compilation
    Par krokmitaine dans le forum C++
    Réponses: 6
    Dernier message: 10/11/2006, 15h06
  5. problème avec sql designer
    Par artotal dans le forum Langage SQL
    Réponses: 1
    Dernier message: 09/08/2006, 16h57

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