void doSomething()
{
if (...)
notify(event2());
else
notify(event3());
}
void notify(const event& e)
{
// Ici, la bonne fonction "handle" sera appelée sur l'objet
// 'listener' en fonction du type réel de l'objet 'e'
m_listener.handle(e);
}
listener& m_listener;
};
Une première idée est l'utilisation de 'typeid' dans une fonction générale
"handle(const event& e)", mais y'a-t-il d'autres solutions ? Il me semble
qu'il est possible de faire la même chose avec un Design Pattern, mais je ne
me souviens plus comment...
Et pourquoi pas une évolution future du langage pour prendre en compte ce
problème ?
Merci pour vos réponses !
Vincent
--
SL> Au fait elle est mieux ma signature maintenant ?
Oui. T'enlève encore les conneries que t'as écrit dedans et c'est bon.
-+- JB in <http://www.le-gnu.net> : Le neuneuttoyage par le vide -+-
Cette action est irreversible, confirmez la suppression du commentaire ?
Signaler le commentaire
Veuillez sélectionner un problème
Nudité
Violence
Harcèlement
Fraude
Vente illégale
Discours haineux
Terrorisme
Autre
Michaël Monerau
Vincent Richard wrote:
Une première idée est l'utilisation de 'typeid' dans une fonction générale "handle(const event& e)", mais y'a-t-il d'autres solutions ? Il me semble qu'il est possible de faire la même chose avec un Design Pattern, mais je ne me souviens plus comment...
Pourquoi ne pas utiliser le polymorphisme dans ce cas ? Tu peux faire ta classe event virtuelle pure avec les fonctions d'interface, que chaque eventX redéfinira. Ensuite, tu n'as même plus besoin de surcharger ta fonction handle dans listener (ce qui s'avère très lourd : imagine que tu rajoutes un event, tu devras ajouter une fonction handle aussi !).
Et pourquoi pas une évolution future du langage pour prendre en compte ce problème ?
Ce problème, si j'ai bien compris, est bien déjà pris en compte par le polymorphisme. Il faut juste que tu revois un peu le design et ça doit passer :) -- <-= Michaël "Cortex" Monerau =->
Vincent Richard wrote:
Une première idée est l'utilisation de 'typeid' dans une fonction
générale "handle(const event& e)", mais y'a-t-il d'autres solutions ?
Il me semble qu'il est possible de faire la même chose avec un Design
Pattern, mais je ne me souviens plus comment...
Pourquoi ne pas utiliser le polymorphisme dans ce cas ? Tu peux faire ta
classe event virtuelle pure avec les fonctions d'interface, que chaque
eventX redéfinira. Ensuite, tu n'as même plus besoin de surcharger ta
fonction handle dans listener (ce qui s'avère très lourd : imagine que tu
rajoutes un event, tu devras ajouter une fonction handle aussi !).
Et pourquoi pas une évolution future du langage pour prendre en
compte ce problème ?
Ce problème, si j'ai bien compris, est bien déjà pris en compte par le
polymorphisme. Il faut juste que tu revois un peu le design et ça doit
passer :)
--
<-= Michaël "Cortex" Monerau =->
Une première idée est l'utilisation de 'typeid' dans une fonction générale "handle(const event& e)", mais y'a-t-il d'autres solutions ? Il me semble qu'il est possible de faire la même chose avec un Design Pattern, mais je ne me souviens plus comment...
Pourquoi ne pas utiliser le polymorphisme dans ce cas ? Tu peux faire ta classe event virtuelle pure avec les fonctions d'interface, que chaque eventX redéfinira. Ensuite, tu n'as même plus besoin de surcharger ta fonction handle dans listener (ce qui s'avère très lourd : imagine que tu rajoutes un event, tu devras ajouter une fonction handle aussi !).
Et pourquoi pas une évolution future du langage pour prendre en compte ce problème ?
Ce problème, si j'ai bien compris, est bien déjà pris en compte par le polymorphisme. Il faut juste que tu revois un peu le design et ça doit passer :) -- <-= Michaël "Cortex" Monerau =->
Patrick Mézard
En fait, tu peux obtenir ce genre de comportement avec un pattern "Visitor" ( http://www.cuj.com/documents/s67/cujjsup1906martin/ ). L'idée c'est qu'au lieu de faire :
void notify(const event& e) { // Ici, la bonne fonction "handle" sera appelée sur l'objet // 'listener' en fonction du type réel de l'objet 'e' m_listener.handle(e); }
on fait plutôt :
void notify(const event& e) { // Ici, 'event' appellera lui même la bonne fonction de 'listener' car il connait son // propre type. e.notify(m_listener); }
Dans le premier cas, le problème est que "m_listener" ne connait pas le type de "e" et c'est à ce moment là que tu as besoin de le déduire. En revanche, "e" connait son type, on peut donc lui déléguer l'action "notify" pour qu'il réalise les opérations souhaitées.
Typiquement dans ton cas tu aurais :
class event1 : public event { public: virtual notify(listener& l) { l->handle(*this); //appelle listener::handle(event1& ) } };
Le gros désavantage de cette technique est que les "event" doivent connaître et avoir accès à l'interface de "listener", ce qui crée pas mal de dépendances désagréables. Après, il existe tout un tas de variantes pour trouver un compromis entre les dépendances statiques et l'utilisation de RTTI. Il y a un chapitre assez complet à ce sujet dans le "Modern C++ Design" d'Alexandrescu
Patrick Mézard
En fait, tu peux obtenir ce genre de comportement avec un pattern "Visitor"
( http://www.cuj.com/documents/s67/cujjsup1906martin/ ). L'idée c'est
qu'au lieu de faire :
void notify(const event& e)
{
// Ici, la bonne fonction "handle" sera appelée sur l'objet
// 'listener' en fonction du type réel de l'objet 'e'
m_listener.handle(e);
}
on fait plutôt :
void notify(const event& e)
{
// Ici, 'event' appellera lui même la bonne fonction de 'listener'
car il connait son
// propre type.
e.notify(m_listener);
}
Dans le premier cas, le problème est que "m_listener" ne connait pas le type
de "e" et c'est à ce moment là que tu as besoin de le déduire. En revanche,
"e" connait son type, on peut donc lui déléguer l'action "notify" pour qu'il
réalise les opérations souhaitées.
Typiquement dans ton cas tu aurais :
class event1 : public event
{
public:
virtual notify(listener& l)
{
l->handle(*this); //appelle listener::handle(event1& )
}
};
Le gros désavantage de cette technique est que les "event" doivent connaître
et avoir accès à l'interface de "listener", ce qui crée pas mal de
dépendances désagréables. Après, il existe tout un tas de variantes pour
trouver un compromis entre les dépendances statiques et l'utilisation de
RTTI. Il y a un chapitre assez complet à ce sujet dans le "Modern C++
Design" d'Alexandrescu
En fait, tu peux obtenir ce genre de comportement avec un pattern "Visitor" ( http://www.cuj.com/documents/s67/cujjsup1906martin/ ). L'idée c'est qu'au lieu de faire :
void notify(const event& e) { // Ici, la bonne fonction "handle" sera appelée sur l'objet // 'listener' en fonction du type réel de l'objet 'e' m_listener.handle(e); }
on fait plutôt :
void notify(const event& e) { // Ici, 'event' appellera lui même la bonne fonction de 'listener' car il connait son // propre type. e.notify(m_listener); }
Dans le premier cas, le problème est que "m_listener" ne connait pas le type de "e" et c'est à ce moment là que tu as besoin de le déduire. En revanche, "e" connait son type, on peut donc lui déléguer l'action "notify" pour qu'il réalise les opérations souhaitées.
Typiquement dans ton cas tu aurais :
class event1 : public event { public: virtual notify(listener& l) { l->handle(*this); //appelle listener::handle(event1& ) } };
Le gros désavantage de cette technique est que les "event" doivent connaître et avoir accès à l'interface de "listener", ce qui crée pas mal de dépendances désagréables. Après, il existe tout un tas de variantes pour trouver un compromis entre les dépendances statiques et l'utilisation de RTTI. Il y a un chapitre assez complet à ce sujet dans le "Modern C++ Design" d'Alexandrescu
Patrick Mézard
Nicolas Fleury
Vincent Richard wrote:
Bonjour et bon week-end !
Je me demandais s'il était possible en C++ de faire la chose suivante :
class event { /* ... */ }
class event1 : public event { /* ... */ }; class event2 : public event { /* ... */ }; class event3 : public event { /* ... */ };
void doSomething() { if (...) notify(event2()); else notify(event3()); }
void notify(const event& e) { // Ici, la bonne fonction "handle" sera appelée sur l'objet // 'listener' en fonction du type réel de l'objet 'e' m_listener.handle(e); }
listener& m_listener; };
Une première idée est l'utilisation de 'typeid' dans une fonction générale "handle(const event& e)", mais y'a-t-il d'autres solutions ? Il me semble qu'il est possible de faire la même chose avec un Design Pattern, mais je ne me souviens plus comment...
Et pourquoi pas une évolution future du langage pour prendre en compte ce problème ?
Merci pour vos réponses !
Une solution que j'ai déjà adoptée pour un système d'événement est:
class SomeEventSource {}; class MyEvent1 : public Event<MyEvent1, SomeEventSource> {}; class MyEvent2 : public Event<MyEvent2, SomeEventSource> {};
Le "curiously recurring template pattern" permet ici d'unifier dans la classe Event la propagation de l'événement. Ta classe Event peut, par exemple, avoir un multimap static source->listener. Le deuxième paramètre de Event permet d'avoir une méthode GetSource pour avoir la source de l'événement. Bref, avec ce code tu te retrouverais avec qqch comme:
class MyClass : public Listener<MyEvent1>, public Listener<MyEvent2> {...};
Mais ce n'est pas nécessairement encore idéal car tu devrais appeler Listener<MyEvent1>::Listen(...). Il est possible d'avoir une seule classe Listener. Supposons que la classe Event n'appelle pas des Listener, mais des Handlers. Tu peux faire une classe Listener qui contient plusieurs Handlers à travers un classe de base non-templatée:
class Listener { class HandlerBase { virtual ~HandlerContainer() = 0; }; template <typename Event_> class TypedHandler : public HandlerBase, public Handler<Event_> { // Seulement besoin du constructeur (listen) et // destructeur (unlisten). };
Et tu pourras ensuite hériter de Listener et faire qqch comme Listen<MyEvent1>(this). Il y a plusieurs autres possibilités, mais que croit que ça donne une idée de quelques techniques.
Tu peux aussi regarder Boost.Signals (www.boost.org) qui permet de faire l'équivalent.
Il y a aussi le livre "Pattern Hatching" qui parle du pattern "Multicast" ou "Typed Message" qui semble être ce qui t'intéresse.
Nicolas
Vincent Richard wrote:
Bonjour et bon week-end !
Je me demandais s'il était possible en C++ de faire la chose suivante :
class event { /* ... */ }
class event1 : public event { /* ... */ };
class event2 : public event { /* ... */ };
class event3 : public event { /* ... */ };
void doSomething()
{
if (...)
notify(event2());
else
notify(event3());
}
void notify(const event& e)
{
// Ici, la bonne fonction "handle" sera appelée sur l'objet
// 'listener' en fonction du type réel de l'objet 'e'
m_listener.handle(e);
}
listener& m_listener;
};
Une première idée est l'utilisation de 'typeid' dans une fonction générale
"handle(const event& e)", mais y'a-t-il d'autres solutions ? Il me semble
qu'il est possible de faire la même chose avec un Design Pattern, mais je ne
me souviens plus comment...
Et pourquoi pas une évolution future du langage pour prendre en compte ce
problème ?
Merci pour vos réponses !
Une solution que j'ai déjà adoptée pour un système d'événement est:
class SomeEventSource {};
class MyEvent1 : public Event<MyEvent1, SomeEventSource> {};
class MyEvent2 : public Event<MyEvent2, SomeEventSource> {};
Le "curiously recurring template pattern" permet ici d'unifier dans la
classe Event la propagation de l'événement. Ta classe Event peut, par
exemple, avoir un multimap static source->listener. Le deuxième
paramètre de Event permet d'avoir une méthode GetSource pour avoir la
source de l'événement. Bref, avec ce code tu te retrouverais avec qqch
comme:
class MyClass : public Listener<MyEvent1>, public Listener<MyEvent2>
{...};
Mais ce n'est pas nécessairement encore idéal car tu devrais appeler
Listener<MyEvent1>::Listen(...). Il est possible d'avoir une seule
classe Listener. Supposons que la classe Event n'appelle pas des
Listener, mais des Handlers. Tu peux faire une classe Listener qui
contient plusieurs Handlers à travers un classe de base non-templatée:
class Listener {
class HandlerBase { virtual ~HandlerContainer() = 0; };
template <typename Event_>
class TypedHandler : public HandlerBase, public Handler<Event_>
{
// Seulement besoin du constructeur (listen) et
// destructeur (unlisten).
};
Et tu pourras ensuite hériter de Listener et faire qqch comme
Listen<MyEvent1>(this). Il y a plusieurs autres possibilités, mais que
croit que ça donne une idée de quelques techniques.
Tu peux aussi regarder Boost.Signals (www.boost.org) qui permet de faire
l'équivalent.
Il y a aussi le livre "Pattern Hatching" qui parle du pattern
"Multicast" ou "Typed Message" qui semble être ce qui t'intéresse.
void doSomething() { if (...) notify(event2()); else notify(event3()); }
void notify(const event& e) { // Ici, la bonne fonction "handle" sera appelée sur l'objet // 'listener' en fonction du type réel de l'objet 'e' m_listener.handle(e); }
listener& m_listener; };
Une première idée est l'utilisation de 'typeid' dans une fonction générale "handle(const event& e)", mais y'a-t-il d'autres solutions ? Il me semble qu'il est possible de faire la même chose avec un Design Pattern, mais je ne me souviens plus comment...
Et pourquoi pas une évolution future du langage pour prendre en compte ce problème ?
Merci pour vos réponses !
Une solution que j'ai déjà adoptée pour un système d'événement est:
class SomeEventSource {}; class MyEvent1 : public Event<MyEvent1, SomeEventSource> {}; class MyEvent2 : public Event<MyEvent2, SomeEventSource> {};
Le "curiously recurring template pattern" permet ici d'unifier dans la classe Event la propagation de l'événement. Ta classe Event peut, par exemple, avoir un multimap static source->listener. Le deuxième paramètre de Event permet d'avoir une méthode GetSource pour avoir la source de l'événement. Bref, avec ce code tu te retrouverais avec qqch comme:
class MyClass : public Listener<MyEvent1>, public Listener<MyEvent2> {...};
Mais ce n'est pas nécessairement encore idéal car tu devrais appeler Listener<MyEvent1>::Listen(...). Il est possible d'avoir une seule classe Listener. Supposons que la classe Event n'appelle pas des Listener, mais des Handlers. Tu peux faire une classe Listener qui contient plusieurs Handlers à travers un classe de base non-templatée:
class Listener { class HandlerBase { virtual ~HandlerContainer() = 0; }; template <typename Event_> class TypedHandler : public HandlerBase, public Handler<Event_> { // Seulement besoin du constructeur (listen) et // destructeur (unlisten). };
Et tu pourras ensuite hériter de Listener et faire qqch comme Listen<MyEvent1>(this). Il y a plusieurs autres possibilités, mais que croit que ça donne une idée de quelques techniques.
Tu peux aussi regarder Boost.Signals (www.boost.org) qui permet de faire l'équivalent.
Il y a aussi le livre "Pattern Hatching" qui parle du pattern "Multicast" ou "Typed Message" qui semble être ce qui t'intéresse.