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.

5 réponses

1 2
Avatar
amerio

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 );
}
};


Techniquement, c'est sur, ca marche.
Je mets de coté le dynamic_cast et les indirections (qui seront ptet un pb, mais bon).
En fait, ce qui me gene, c'est l'héritage pour gérer un message.
Disons que c'est TargetAB qui contient les données qui seront affectées par MessageA, pas
TargetA.
Et pareil pour MessageB. Je vais donner un exemple.
Admettons que MessageA doivent masquer mon objet Target, et MessageB le rende visible.
Ces deux messages adressent en fait le même membre m_isVisible de TargetAB.
On pourrait dire qu'il s'agit du même message mais avec un parametre, mais d'autre msg ne
sont pas aussi simple.
On pourrait aussi faire en sorte que TargetA ou B soit capable de s'upcaster en TargetAB,
mais le coup deviendra vite prohibitif (sans parler de la lourdeur d'ecriture).
Et que dire du cas ou je veux une nouvelle Target dérivant de TargetAB mais surchargant le
traitement de A. Un TargetABA.

Bon, je pose peut être un pb insoluble en c++ (ou du moins avec mes contraintes).
Sinon, c'etait une bonne solution ;-)

Avatar
Jean-Marc Bourguet
"amerio" writes:

Je mets de coté le dynamic_cast et les indirections (qui seront ptet
un pb, mais bon).

En fait, ce qui me gene, c'est l'héritage pour gérer un message.
Disons que c'est TargetAB qui contient les données qui seront
affectées par MessageA, pas TargetA. Et pareil pour MessageB. Je
vais donner un exemple.

Admettons que MessageA doivent masquer mon objet Target, et MessageB
le rende visible. Ces deux messages adressent en fait le même
membre m_isVisible de TargetAB. On pourrait dire qu'il s'agit du
même message mais avec un parametre, mais d'autre msg ne sont pas
aussi simple. On pourrait aussi faire en sorte que TargetA ou B
soit capable de s'upcaster en TargetAB, mais le coup deviendra vite
prohibitif (sans parler de la lourdeur d'ecriture). Et que dire du
cas ou je veux une nouvelle Target dérivant de TargetAB mais
surchargant le traitement de A. Un TargetABA.


Si tu as plusieurs messages lies, rien ne t'empeche de faire

class TargetAB: virtual public Target
{
public:
virtual void HandleMessageA(MessageA&);
virtual void HandleMessageB(MessageB&);
};

MessageA::dispatch(Target* t)
{
TargetAB* tab = dynamic_cast<TargetAB*>(t);
if (tab)
tab->HandleMessageA(*this);
else
t->HandleMessage(*this);
}

MessageB::dispatch(Target* t)
{
TargetAB* tab = dynamic_cast<TargetAB*>(t);
if (tab)
tab->HandleMessageB(*this);
else
t->HandleMessage(*this);
}

Bon, je pose peut être un pb insoluble en c++ (ou du moins avec mes
contraintes). Sinon, c'etait une bonne solution ;-)


Tu connais une bonne solution dans un autre langage?

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
Christophe Lephay
amerio wrote:

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 );
}
};


Techniquement, c'est sur, ca marche.
Je mets de coté le dynamic_cast et les indirections (qui seront ptet
un pb, mais bon).
En fait, ce qui me gene, c'est l'héritage pour gérer un message.
Disons que c'est TargetAB qui contient les données qui seront
affectées par MessageA, pas TargetA.
Et pareil pour MessageB. Je vais donner un exemple.
Admettons que MessageA doivent masquer mon objet Target, et MessageB
le rende visible. Ces deux messages adressent en fait le même membre
m_isVisible de TargetAB.
On pourrait dire qu'il s'agit du même message mais avec un parametre,
mais d'autre msg ne sont pas aussi simple.
On pourrait aussi faire en sorte que TargetA ou B soit capable de
s'upcaster en TargetAB, mais le coup deviendra vite prohibitif (sans
parler de la lourdeur d'ecriture).
Et que dire du cas ou je veux une nouvelle Target dérivant de
TargetAB mais surchargant le traitement de A. Un TargetABA.

Bon, je pose peut être un pb insoluble en c++ (ou du moins avec mes
contraintes).
Sinon, c'etait une bonne solution ;-)


On peut automatiser avec les templates. J'ai fait un petit test rapide avec
les classes suivantes, et ça a l'air de marcher :

class MessageHandler;
class Message
{
public:
virtual void notify( MessageHandler * mh );
};

void Message::notify( MessageHandler * mh )
{
mh->HandleMessage( *this );
}

lass MessageHandler
{
public:
virtual void HandleMessage( const Message& ) {}
};

template< class TypeMessage >
class SpecificHandler : virtual public MessageHandler
{
public:
virtual void HandleMessage( const TypeMessage& )=0;
};


Que j'utilise comme suit :

// Création d'un nouveau type de message :
class MessageA : public Message
{
public:
void notify( MessageHandler * mh )
{
SpecificHandler< MessageA > * tmh = dynamic_cast< SpecificHandler<
MessageA > * >( mh );
// instanciation d'un nouveau type de Handler pour ce type de message
// dont devra hériter tout objet désireux de gérer des MessageA

if( tmh )
tmh->HandleMessage( *this );
else
mh->HandleMessage( *this );
}
};


// création d'un nouveau type qui doit gérer les MessageA
// doit donc hériter de SpecificHandler< MessageA >
class MessageAHandler : public SpecificHandler< MessageA >
{
public:
// l'écriture du gestionnaire idoine
void HandleMessage( const MessageA& msg )
{ std::cout<< "message reçu" << std::endl; }
};

En testant chez moi, ça a l'air de marcher. Il reste qu'il faut écrire à peu
près à l'identique notify. Il se peut qu'on puisse l'automatiser avec un
template, mais faut voir si la solution ne devient pas alors plus
compliquée...

Chris


Avatar
Christophe Lephay
amerio wrote:

Techniquement, c'est sur, ca marche.
Je mets de coté le dynamic_cast et les indirections (qui seront ptet
un pb, mais bon).
En fait, ce qui me gene, c'est l'héritage pour gérer un message.
Disons que c'est TargetAB qui contient les données qui seront
affectées par MessageA, pas TargetA.
Et pareil pour MessageB. Je vais donner un exemple.
Admettons que MessageA doivent masquer mon objet Target, et MessageB
le rende visible. Ces deux messages adressent en fait le même membre
m_isVisible de TargetAB.
On pourrait dire qu'il s'agit du même message mais avec un parametre,
mais d'autre msg ne sont pas aussi simple.
On pourrait aussi faire en sorte que TargetA ou B soit capable de
s'upcaster en TargetAB, mais le coup deviendra vite prohibitif (sans
parler de la lourdeur d'ecriture).
Et que dire du cas ou je veux une nouvelle Target dérivant de
TargetAB mais surchargant le traitement de A. Un TargetABA.


Avec les classes que j'ai posté par ailleurs, celà donnerait :

class MessageA : public Message
{
public:
void notify( MessageHandler * mh )
{
SpecificHandler< MessageA > * tmh = dynamic_cast< SpecificHandler<
MessageA > * >( mh );
if( tmh )
tmh->HandleMessage( *this );
else
mh->HandleMessage( *this );
}
};

class MessageB : public Message
{
public:
void notify( MessageHandler * mh )
{
SpecificHandler< MessageB > * tmh = dynamic_cast< SpecificHandler<
MessageB > * >( mh );
if( tmh )
tmh->HandleMessage( *this );
else
mh->HandleMessage( *this );
}
};


class TargetAB : public SpecificHandler< MessageA >, public SpecificHandler<
MessageB >
{
public:
void HandleMessage( const MessageA& ) { visibleúlse; Draw(); }
void HandleMessage( const MessageB& ) { visible=true; Draw(); }

private:
bool visible;
void Draw(); // à priori virtuelle dans une classe de base
};

Bon, je pose peut être un pb insoluble en c++ (ou du moins avec mes
contraintes).


Insoluble, tout dépend de l'objectif visé. La solution proposée par
Jean-Marc marche si l'objectif est de pouvoir étendre les différentes
hiérarchies sans modifier le code existant. Par contre, elle échoue dans la
mesure où elle ne dispense pas d'écrire du code supplémentaire aux seuls
gestionnaires d'évènements (mais l'écriture du code dans les nouvelles
classes messages s'amortit largement, il me semble : juste quelques lignes
obtenues le cas échéant via un copier-coller)...

Chris

Avatar
Fabien LE LEZ
On Mon, 03 Nov 2003 13:09:18 GMT, "amerio" wrote:

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


Tout ça serait encore plus simple si tu choisissais un autre langage
que C++ (en particulier, un langage interprété) ! ;-)

--
;-)

1 2