OVH Cloud OVH Cloud

Hierarchie de message et de target

15 réponses
Avatar
amerio
Bonjour,
Je cherche a implémenter un système de message/target supportant héritage.
Dans le principe : on a une hiérarchie de Message, et une hiérarchie de Target acceptant
certains Message.

class Message { ... } // classe de base
class MessageSpecial : public Message { ... }
class MessageSpecialExt : public MessageSpecial { ... }

La classe de base des Target doit ressembler à ca :
class Target
{
Target();
virtual ~Target();
virtual bool OnMessage(Message& msg); // le point de reception public

protected:
virtual bool HandleMessage(Message& msg); // traitement du msg de base
}

Ce dont j'ai besoin :
class TargetSpecial : public Target
{
TargetSpecial();
virtual ~TargetSpecial();

protected:
virtual bool HandleMessageSpecial(MessageSpecial& msg);
}

A noter que je *veux* éviter d'avoir HandleMessageSpecial(Message& msg)
(pour éviter un cast a chaque fois, et c'est ça qui est dur)

J'ai vu l'exemple de Meyers dans More Effective C++, mais il est inapplicable pour moi :
- ça oblige a faire un cast (et je veux éviter) : HandleMessageSpecial(Message& msg)
- l'utilisation d'une map ne permet pas de gérer une hiérarchie de message (si j'ajoute
une nouveau message mais pas de handler associé, il sera ignorer et non traité, car non
trouvé dans le map)

J'ai perdu ma journée la dessus, alors toute idée est bienvenue.

10 réponses

1 2
Avatar
Christophe Lephay
amerio wrote:
Bonjour,
Je cherche a implémenter un système de message/target supportant
héritage.
Dans le principe : on a une hiérarchie de Message, et une hiérarchie
de Target acceptant certains Message.

class Message { ... } // classe de base
class MessageSpecial : public Message { ... }
class MessageSpecialExt : public MessageSpecial { ... }

La classe de base des Target doit ressembler à ca :
class Target
{
Target();
virtual ~Target();
virtual bool OnMessage(Message& msg); // le point de reception
public

protected:
virtual bool HandleMessage(Message& msg); // traitement du msg
de base }

Ce dont j'ai besoin :
class TargetSpecial : public Target
{
TargetSpecial();
virtual ~TargetSpecial();

protected:
virtual bool HandleMessageSpecial(MessageSpecial& msg);
}

A noter que je *veux* éviter d'avoir HandleMessageSpecial(Message&
msg) (pour éviter un cast a chaque fois, et c'est ça qui est dur)


class Message
{
public:
virtual bool notify( Target * the_target ) { return
the_target->HandleMessage( *this ); } // *this est de type Message
};

class MessagePasSpecial : public Message
{
public:
// on ne redéfinit pas notify si on veut que HandleMessage( Message& ) gère
le message
};

class MessageSpecial : public Message
{
public:
bool notify( Target * the_target ) { return the_target->HandleMessage(
*this ); } // *this est de type MessageSpecial
};

class Target
{
public:
virtual bool HandleMessage( Message& );
};

class TargetSpecial : public Target
{
public:
virtual bool HandleMessage( MessageSpecial& );
};

Ensuite, pour gérer un message :

TargetSpecial ts;
Message * msg = GetNextMessage();
msg->notify( &ts );

Si msg pointe vers un MessageSpecial, notify devrait appeler
TargetSpecial::HandleMessage( MessageSpecial& ). Si il pointe vers un
Message ou MessagePasSpecial, notify appelle TargetSpecial::HandleMessage(
Message& )...

J'ai pas testé, et en plus il est tard. Si je suis à coté de la plaque, fais
des recherches sur double dispatch (ou pattern visitor), puisqu'il me semble
que c'est celà que tu veux. Note qu'en même temps, c'est ce dont parle
Meyers à ce qu'il me semble aussi...

Demain, il y fera plus clair :)

Chris

Avatar
Christophe Lephay
Christophe Lephay wrote:
Je cherche a implémenter un système de message/target supportant
héritage.
Dans le principe : on a une hiérarchie de Message, et une hiérarchie
de Target acceptant certains Message.
...


A noter que je *veux* éviter d'avoir HandleMessageSpecial(Message&
msg) (pour éviter un cast a chaque fois, et c'est ça qui est dur)


class Message
{
public:
virtual bool notify( Target * the_target ) { return
the_target->HandleMessage( *this ); } // *this est de type Message
};

class MessagePasSpecial : public Message
{
public:
// on ne redéfinit pas notify si on veut que HandleMessage( Message&
) gère le message
};

class MessageSpecial : public Message
{
public:
bool notify( Target * the_target ) { return the_target->HandleMessage(
*this ); } // *this est de type MessageSpecial
};
...

Demain, il y fera plus clair :)


Sans attendre demain, je vois que mon code est à coté de la plaque...

Je ré-essaie demain (à priori, il faut une hiérarchie supplémentaire si on
veut pas de casts)..

Chris


Avatar
Fabien LE LEZ
Tu peux peut-être essayer d'aller voir du côté des templates :

template <class Msg> class TargetT: virtual public Target
{
virtual bool HandleMessage(Message& msg)
{
return HandleMessageSpecial (dynamic_cast<Msg&>(msg));
};
virtual bool HandleMessageSpecial (Msg& msg);
};

class TargetSpecial : public TargetT<MessageSpecial>
{
bool HandleMessageSpecial (MessageSpecial& msg);
};


--
;-)
Avatar
Christophe Lephay
Fabien LE LEZ wrote:
Tu peux peut-être essayer d'aller voir du côté des templates :

template <class Msg> class TargetT: virtual public Target
{
virtual bool HandleMessage(Message& msg)
{
return HandleMessageSpecial (dynamic_cast<Msg&>(msg));
};
virtual bool HandleMessageSpecial (Msg& msg);
};

class TargetSpecial : public TargetT<MessageSpecial>
{
bool HandleMessageSpecial (MessageSpecial& msg);
};


Mais ça se complique si on veut qu'un Target gère plusieurs types différents
de messages. Je crois que c'est ce que veut faire Amerio.

Borland et Microsoft définissaient des tas de macros pour arriver à celà...

Chris

Avatar
Fabien LE LEZ
On Mon, 3 Nov 2003 11:47:06 +0100, "Christophe Lephay"
wrote:

class TargetSpecial : public TargetT<MessageSpecial>
{
bool HandleMessageSpecial (MessageSpecial& msg);
};


Mais ça se complique si on veut qu'un Target gère plusieurs types différents
de messages.


class TargetVraimentSpecial : public TargetT<AutreMessageSpecial>,
public TargetSpecial
{
bool HandleMessageSpecial (AutreMessageSpecial& msg);
bool HandleMessageSpecial (MessageSpecial& msg);
};

Un peu lourd à écrire il est vrai...


Avatar
Christophe Lephay
Fabien LE LEZ wrote:
On Mon, 3 Nov 2003 11:47:06 +0100, "Christophe Lephay"
wrote:

class TargetSpecial : public TargetT<MessageSpecial>
{
bool HandleMessageSpecial (MessageSpecial& msg);
};


Mais ça se complique si on veut qu'un Target gère plusieurs types
différents de messages.


class TargetVraimentSpecial : public TargetT<AutreMessageSpecial>,
public TargetSpecial
{
bool HandleMessageSpecial (AutreMessageSpecial& msg);
bool HandleMessageSpecial (MessageSpecial& msg);
};

Un peu lourd à écrire il est vrai...


Et laquelle de ces deux fonctions doit être appelée par
HandleMessage(Message& msg), dont la définition était (pour rappel) :

virtual bool HandleMessage(Message& msg)
{
return HandleMessageSpecial (dynamic_cast<Msg&>(msg));
}

Le problème est que dans une même classe, une fonction différente doit
pouvoir être appelée selon le type de message reçu (double dispatch).

Par ailleurs, il y a une question sémantique qui se pose (la question
concerne plus Amerio que toi) :

class message {...};
class messageA : public message { ... };
class messageB : public messageA { ... };

et on a une classe target avec :

class target
{
bool HandleMessage( message& );
bool HandleMessage( messageA &); // on gère les messageA de manière
spécifique
};

Laquelle de ces deux fonctions doit-elle être appelée dans le cas où un
MessageB est transmis ?

En fait, je pense de plus en plus qu'il n'y a pas de solution satisfaisant
les désiderata d'Amerio - la plus proche, basée sur le pattern Visitor
nécessitant que les fonctions susceptibles de traiter les différents types
de messages soient connues par la classe de base (et donc que les types de
messages forment un ensemble fini et connu à la compilation).

Au cas où la solution existerait, je pense qu'elle se trouverait dans le GoF
(mais peut-être s'y trouve-t-elle déjà ailleurs ?). Je pense qu'une telle
solution ne peut qu'être un compromis, et quitte donc un peu le domaine de
la solution générale ou universelle.

J'ai rien de mieux à faire aujourd'hui que d'y refléchir, mais il serait
interessant que les grands nous soumettent les solutions, compromis ou
alternatives du double dispatch qu'ils ont décider de mettre en oeuvre au
long de leurs longues années de pratique...

Chris



Avatar
Jean-Marc Bourguet
"amerio" writes:

Je cherche a implémenter un système de message/target supportant
héritage. Dans le principe : on a une hiérarchie de Message, et une
hiérarchie de Target acceptant certains Message.


Si ce qui se trouve dans ne te
conviens pas, peux-tu expliquer pourquoi, qu'on cherche des
alternatives.

A+
--
Jean-Marc
FAQ de fclc++: http://www.cmla.ens-cachan.fr/~dosreis/C++/FAQ
C++ FAQ Lite en VF: http://www.ifrance.com/jlecomte/c++/c++-faq-lite/index.html
Site de usenet-fr: http://www.usenet-fr.news.eu.org

Avatar
amerio
Jean-Marc Bourguet wrote:
"amerio" writes:

Je cherche a implémenter un système de message/target supportant
héritage. Dans le principe : on a une hiérarchie de Message, et une
hiérarchie de Target acceptant certains Message.


Si ce qui se trouve dans ne te
conviens pas, peux-tu expliquer pourquoi, qu'on cherche des
alternatives.


Je n'ai pas acces a cet article. Peut tu le resumer ici ou me faire un lien par google
news ?


Avatar
amerio
Christophe Lephay wrote:
Fabien LE LEZ wrote:
On Mon, 3 Nov 2003 11:47:06 +0100, "Christophe Lephay"
wrote:

class TargetSpecial : public TargetT<MessageSpecial>
{
bool HandleMessageSpecial (MessageSpecial& msg);
};


Mais ça se complique si on veut qu'un Target gère plusieurs types
différents de messages.




Oui. Et plein !

class TargetVraimentSpecial : public TargetT<AutreMessageSpecial>,
public TargetSpecial
{
bool HandleMessageSpecial (AutreMessageSpecial& msg);
bool HandleMessageSpecial (MessageSpecial& msg);
};

Un peu lourd à écrire il est vrai...



Bcp trop lourd. Surtout qu'un Target peut gerer une dizaine de message.

Et laquelle de ces deux fonctions doit être appelée par
HandleMessage(Message& msg), dont la définition était (pour rappel) :

virtual bool HandleMessage(Message& msg)
{
return HandleMessageSpecial (dynamic_cast<Msg&>(msg));
}

Le problème est que dans une même classe, une fonction différente doit
pouvoir être appelée selon le type de message reçu (double dispatch).

Par ailleurs, il y a une question sémantique qui se pose (la question
concerne plus Amerio que toi) :

class message {...};
class messageA : public message { ... };
class messageB : public messageA { ... };

et on a une classe target avec :

class target
{
bool HandleMessage( message& );
bool HandleMessage( messageA &); // on gère les messageA de manière
spécifique
};

Laquelle de ces deux fonctions doit-elle être appelée dans le cas où un
MessageB est transmis ?


bool HandleMessage( MessageA &);
C'est la plus proche (MessageB est plus proche de MessageA que de Message)
(a noter qu'en pratique, la classe de base Message doit être virtuelle pure)

En fait, je pense de plus en plus qu'il n'y a pas de solution satisfaisant
les désiderata d'Amerio - la plus proche, basée sur le pattern Visitor
nécessitant que les fonctions susceptibles de traiter les différents types
de messages soient connues par la classe de base (et donc que les types de
messages forment un ensemble fini et connu à la compilation).


Ce n'est pas le cas. Je dois pouvoir ajouter un msg tres facilement, en recompilant juste
la Target qui traite ce message.
(en fait, c'est même le principe de base du msg : découpler au maximum l'emetteur du msg,
de celui qui fait transiter le msg, de celui qui traite au final)
Pour résumé :
Si Emiter emet le message M, il passe M a un gestionnaire G, qui contient des Target.
Chaque Target T va ensuite essayer de traiter le msg. Mais Emiter ne parle pas a T
directement (il ne sait meme pas ce qu'est une Target !)

Au cas où la solution existerait, je pense qu'elle se trouverait dans le GoF
(mais peut-être s'y trouve-t-elle déjà ailleurs ?). Je pense qu'une telle
solution ne peut qu'être un compromis, et quitte donc un peu le domaine de
la solution générale ou universelle.


Pas trouvé dedans. J'ai ptet vu trop vite, mais a part le visitor, qu'est ce qui pourrait
correspondre ?
Ou alors je l'applique mal a mon cas ?

J'ai rien de mieux à faire aujourd'hui que d'y refléchir, mais il serait
interessant que les grands nous soumettent les solutions, compromis ou
alternatives du double dispatch qu'ils ont décider de mettre en oeuvre au
long de leurs longues années de pratique...


Tout ca serait tellement plus simple si typeof existait et si on pouvait stocker des
typeof dans un conteneur ;-)




Avatar
Christophe Lephay
amerio wrote:
Jean-Marc Bourguet wrote:
"amerio" writes:

Je cherche a implémenter un système de message/target supportant
héritage. Dans le principe : on a une hiérarchie de Message, et une
hiérarchie de Target acceptant certains Message.


Si ce qui se trouve dans ne te
conviens pas, peux-tu expliquer pourquoi, qu'on cherche des
alternatives.


Je n'ai pas acces a cet article. Peut tu le resumer ici ou me faire
un lien par google news ?


Voilà le lien sur google :
http://groups.google.fr/groups?qIkjqa.f11.ln%40news.bourguet.org&hl=fr&lr=&ie=UTF-8&selmÖ651fb6.0211120203.3b72b537%40posting.google.com&rnum=1

ou :

http://tinyurl.com/tfom

En gros, celà reviendrait à créer

// pour gérer les MessageA
class TargetA : virtual public Target
{
public:
virtual void HandleMessageA( MessageA& );
};

// pour gérer les MessageB
class TargetB : virtual public Target
{
public:
virtual void HandleMessageB( MessageB& );
};

// Pour gérer les MessageA et MessageB
class TargetAB : public TargetA, public TargetB
{
void HandleMessageA( MessageA& );
void HandleMessageB( MessageB& );
};

et avec :

Message
{
public:
virtual void dispatch( Target * t ) { t->HandleMessage( *this ); }
};

MessageA : public Message
{
void dispatch( Target * t )
{
TargetA * ta = dynamic_cast< TargetA * >( t );
if( ta )
ta->HandleMessageA( *this );
else
t->HandleMessage( *this );
}
};

MessageB : public Message
{
void dispatch( Target * t )
{
TargetBA * ta = dynamic_cast< TargetB * >( t );
if( ta )
ta->HandleMessageB( *this );
else
t->HandleMessage( *this );
}
};

Chris



1 2