Twitter iPhone pliant OnePlus 11 PS5 Disney+ Orange Livebox Windows 11

Appel dynamique de fonction surchargée : évolution ?

3 réponses
Avatar
Vincent Richard
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 { /* ... */ };

class listener
{
// ...

void handle(const event1& e);
void handle(const event2& e);
void handle(const event3& e);
};

class object
{
// ...

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 -+-

3 réponses

Avatar
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 =->

Avatar
Patrick Mézard
En fait, tu peux obtenir ce genre de comportement avec un pattern "Visitor"
( http://www.cuj.com/documents/s„67/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
Avatar
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 { /* ... */ };

class listener
{
// ...

void handle(const event1& e);
void handle(const event2& e);
void handle(const event3& e);
};

class object
{
// ...

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