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

Détection du type de classe à utiliser

9 réponses
Avatar
Michael
Bonjour à tous,

toujours dans mes petits soucis, voici quel est mon problème aujourd'hui:


class ConnectBase
{
};

class ConnectToFoo1 : public ConnectBase
{
public:
virtual void Connect(foo1 & f) = 0;
};

class ConnectToFoo2 : public ConnectBase
{
public:
virtual void Connect(foo2 & f) = 0;
};

class Capture
{
private:
foo1 f1;
foo2 f2;
public:
void Connect(ConnectBase * c)
{
ConnectToFoo1 * a = dynamic_cast<ConnectToFoo1 *>(c);
if (a)
a->Connect(f1);
else
{
ConnectToFoo2 * b = dynamic_cast<ConnectToFoo2 *>(c);
if (b)
b->Connect(f2);
}
}
};

Est-ce qu'il n'y aurait pas moyen de faire mieux pour la fonction
Capture::Connect??

Merci d'avance

Mike

9 réponses

Avatar
Sylvain Togni
class ConnectBase
{
};

class ConnectToFoo1 : public ConnectBase
{
public:
virtual void Connect(foo1 & f) = 0;
};

class ConnectToFoo2 : public ConnectBase
{
public:
virtual void Connect(foo2 & f) = 0;
};

class Capture
{
private:
foo1 f1;
foo2 f2;
public:
void Connect(ConnectBase * c)
{
ConnectToFoo1 * a = dynamic_cast<ConnectToFoo1 *>(c);
if (a)
a->Connect(f1);
else
{
ConnectToFoo2 * b = dynamic_cast<ConnectToFoo2 *>(c);
if (b)
b->Connect(f2);
}
}
};

Est-ce qu'il n'y aurait pas moyen de faire mieux pour la fonction
Capture::Connect??


Sans changer les classes Connect, non, je crois pas.
Sinon je verrais bien quelque chose comme ça :

class ConnectBase
{
public:
virtual void Connect(foo1 & f) {}
virtual void Connect(foo2 & f) {}
};

[...]

class Capture
{
private:
foo1 f1;
foo2 f2;
public:
void Connect(ConnectBase * c)
{
c->Connect(f1);
c->Connect(f2);
}
};

--
Sylvain Togni

Avatar
Sylvain
Michael wrote on 27/06/2006 17:44:

class ConnectBase
{
ici, je mettrais bien un:

virtual void Connect(foo& f) = 0;
};

class ConnectToFoo{1|2} : public ConnectBase {
public:
virtual void Connect(foo{1| 2}& f) = 0;
};


btw, who's foo ?... supposons:

class foo {};
class foo1 : public foo {};
class foo2 : public foo {};

class Capture {
private:
foo1 f1;
foo2 f2;
public:


introduisons:
foo* getConnector(ConnectBase* c){
if (dynamic_cast<ConnectToFoo1*>(c))
return &f1;
if (dynamic_cast<ConnectToFoo2*>(c))
return &f2;
return null;
}
si tous les cas sont prévus, on retournera un foo&

void Connect(ConnectBase * c)


devient:
foo* f = getConnector(c);
if (f)
c->Connect(*f);

ou, hypothèse précédente:

c->Connect(getConnector(c));

isoler la recherche du connecteur facilitera sa maintenance et
factorisera cette recherche qui ne sera surement pas unique.

Sylvain.

Avatar
Michael
Malheureusement, foo1 et foo2 n'ont pas de base de commune, je n'aurai pas
du choisir des noms aussi proches...

Sinon je me suis dit qu'avec des template je pourrais peut-être m'en
sortir:

class Camera : public ConnectToFoo
{

};

class Capture
{
private:
Foo foo;
Fiv fiv;
public:
template <class ConnectType>
void Connect(ConnectType * plug);

template <>
void Connect(ConnectToFoo * plug)
{
plug->Connect(foo);
}

template <>
void Connect(ConnectToFiv * plug)
{
plug->Connect(fiv);
}
};

Mais le lieur me sort une erreur pour le code suivant:

Capture * cap = new Capture();
Camera * cam = new Camera();
cap->Connect(cam);

Il me sort:

[Linker Error] Unresolved external 'void Capture::Connect<Camera>(Camera
*)' referenced from D:BORLANDCBUILDER6PROJECTSTESTSTEST TEMPLATE
VIDEOAFFICHUNIT1.OBJ

[ Je suis sous BCB6 ]
Avatar
Sylvain
Michael wrote on 28/06/2006 02:47:
Malheureusement, foo1 et foo2 n'ont pas de base de commune, je n'aurai pas
du choisir des noms aussi proches...


il ne tient qu'à toi !

tes ConnectToFoo/i/ font certainement qlq chose de proche avec
l'interface reçue - on cherchera, je pense, à ne pas rendre
l'architecture trop DX (ni MS) dépendante, se privant d'un

class ConnectBase {
virtual void Connect(IUnknown&) = null;
};

mais il est surement possible d'introduire un ancêtre arbitraire commun
(quitte à multi-hériter en suite dans les classes concrêtes Connect2Foo

...

en fait, c'est toujours possible, le problème est que si cela revient à
créer un modèle vide sans aucun dégrossisage du boulot dans les classes
de bases cela perd de son intérêt.

quand je cherchais à définir un modèle propre pour gérer différentes
sources et les rendre dans un visualisateur unique, j'avais à traiter
d'un flux web-cam DX, de la sortie d'un appareil photo numérique (raw
data, API crado et propriétaire) et un capteur d'empreintes capacitif
(bitmap, API prop); après plusieurs jours à essayer de les fédérer, j'ai
codé chaque couple séparement en moins de temps ! (je découvrais
également DX, je ne le referais surement pas de la même façon).

si tu traites de flux plus ou moins identiques - et surtout assimilable
à une interface DX, quitte à coder une coclass en cas de besoin - cela
devrait être plus simple.

note aussi que l'utilisateur a le droit de savoir ce qu'il cherche à
connecter (un input, un filtre, un grabber, un renderer, ...), la classe
Capture pourrait dès lors proposer connectStream(), connectFilter(),
etc, où chaque function sait quel foo, fiv, ..., doit être utilisé.

bon courage ;)
Sylvain.

Avatar
Sylvain Togni
class Camera : public ConnectToFoo
{

};

class Capture
{
private:
Foo foo;
Fiv fiv;
public:
template <class ConnectType>
void Connect(ConnectType * plug);

template <>
void Connect(ConnectToFoo * plug)
{
plug->Connect(foo);
}

template <>
void Connect(ConnectToFiv * plug)
{
plug->Connect(fiv);
}
};


Pas besoin de template ici, la surcharge de fonction suffit.

--
Sylvain Togni

Avatar
kanze
Michael wrote:

toujours dans mes petits soucis, voici quel est mon problème
aujourd'hui:

class ConnectBase
{
};


Avec en fait au moins une fonction virtuelle, j'espère. (Le
destructeur, peut-être, s'il n'y a pas d'autre.)

class ConnectToFoo1 : public ConnectBase
{
public:
virtual void Connect(foo1 & f) = 0;
};

class ConnectToFoo2 : public ConnectBase
{
public:
virtual void Connect(foo2 & f) = 0;
};

class Capture
{
private:
foo1 f1;
foo2 f2;
public:
void Connect(ConnectBase * c)
{
ConnectToFoo1 * a = dynamic_cast<ConnectToFoo1 *>(c);


Sans fonction virtuelle dans ConnectBase, le dynamic_cast ne
marche pas.

if (a)
a->Connect(f1);
else
{
ConnectToFoo2 * b = dynamic_cast<ConnectToFoo2 *>(c);
if (b)
b->Connect(f2);
}
}
};

Est-ce qu'il n'y aurait pas moyen de faire mieux pour la
fonction Capture::Connect??


Est-ce que tu pourrais ajouter une fonction virtuelle dans
ConnectBase ? Quelque chose du genre : linkToCapture. Ensuite,
Capture::Connect appel cette fonction, avec this comme
paramètre, et la supplantation de cette fonction dans la classe
dérivée de ConnectBase appelle une fonction Connect de Capture
avec son this, qui aurait le type de la classe dérivée :

class Capture ;

class ConnectBase
{
public:
virtual void linkToCapture( Capture* capture ) = 0 ;
} ;

// Même chose pour ConnectToFoo2...

class Capture
{
// ...
public:
void connect( ConnectBase* c )
{
c->linkToCapture( this ) ;
}
void connect( ConnectToFoo1* c )
{
c->connect( f1 ) ;
}
void connect( ConnectToFoo2* c )
{
c->connect( f2 ) ;
}
} ;

class ConnectToFoo1
{
public:
virtual void linkToCapture( Capture* capture )
{
capture->connect( this ) ;
}
} ;

C'est un peu dommage d'ajouter la dépendence sur Capture dans
l'hiérarchie des Connect, mais c'est probablement la solution la
plus simple. (Sinon, il y a des solutions à base d'un
std::map< std::type_info*, ... > statique. Mais elles sont
toutes assez lourdes à mettre en oeuvre.)

--
James Kanze GABI Software
Conseils en informatique orientée objet/
Beratung in objektorientierter Datenverarbeitung
9 place Sémard, 78210 St.-Cyr-l'École, France, +33 (0)1 30 23 00 34

Avatar
kanze
Michael wrote:
Malheureusement, foo1 et foo2 n'ont pas de base de commune, je
n'aurai pas du choisir des noms aussi proches...

Sinon je me suis dit qu'avec des template je pourrais
peut-être m'en sortir:


Mais ça suppose une résolution au moment de la compilation. Et
si le type dynamique de Connect est connu lors de l'appel à
Capture::connect, le surcharge classique fait l'affaire ; il
n'y a besoin ni de templates ni de fonctions virtuelles.

class Camera : public ConnectToFoo
{

};

class Capture
{
private:
Foo foo;
Fiv fiv;
public:
template <class ConnectType>
void Connect(ConnectType * plug);

template <>
void Connect(ConnectToFoo * plug)
{
plug->Connect(foo);
}


Je ne suis pas sûr qu'on a le droit de définir une
spécialisation explicite dans la classe.

template <>
void Connect(ConnectToFiv * plug)
{
plug->Connect(fiv);
}
};

Mais le lieur me sort une erreur pour le code suivant:

Capture * cap = new Capture();
Camera * cam = new Camera();
cap->Connect(cam);

Il me sort:

[Linker Error] Unresolved external 'void Capture::Connect<Camera>(Camera
*)' referenced from D:BORLANDCBUILDER6PROJECTSTESTSTEST TEMPLATE
VIDEOAFFICHUNIT1.OBJ


Oui, parce que tu n'as fourni ni une spécialisation de
Capture::connect pour Camera, ni une implémentation générique.

Si tu ne veux appeler la fonction qu'avec des pointeurs à des
dérivées, le surcharge classique fait bien l'affaire. C'est
d'ailleur la principe à la base de la solution que j'ai posté ;
en faisant appeler la fonction depuis une fonction virtuelle de
ConnectBase, on l'appel avec un pointeur qui a le type de la
dérivée, et du coup, la résolution du surcharge s'occupe de la
reste.

--
James Kanze GABI Software
Conseils en informatique orientée objet/
Beratung in objektorientierter Datenverarbeitung
9 place Sémard, 78210 St.-Cyr-l'École, France, +33 (0)1 30 23 00 34

Avatar
kanze
Sylvain wrote:
Michael wrote on 28/06/2006 02:47:

Malheureusement, foo1 et foo2 n'ont pas de base de commune,
je n'aurai pas du choisir des noms aussi proches...


il ne tient qu'à toi !


S'il en est auteur. Si les deux classes viennent de deux
bibliothèques tièrces différentes, en revanche...

Et en quoi est-ce que ça résoud sa problème : il a deux objets
différents, et il s'agit de choisir l'objet même à passer comme
paramètre ? (Sinon, effectivement, c'est probablement plus
propre dans l'ensemble. Et si on ne peut pas toucher à les
classes mêmes, pour une raison ou une autre, il y a des modèles
de conception, genre façade, adaptateur ou passerelle, qui
pourrait servir.)

tes ConnectToFoo/i/ font certainement qlq chose de proche avec
l'interface reçue - on cherchera, je pense, à ne pas rendre
l'architecture trop DX (ni MS) dépendante, se privant d'un

class ConnectBase {
virtual void Connect(IUnknown&) = null;
};

mais il est surement possible d'introduire un ancêtre
arbitraire commun (quitte à multi-hériter en suite dans les
classes concrêtes Connect2Foo


C'est aussi une possiblité... qui renvoie le dynamic_cast dans
les Connect (mais qui n'exige jamais une chaîne de
dynamic_cast). Mais qui ne résoud pas non plus comment savoir
lequel des deux objets à passer.

On pourrait éventuellement définir un struct avec les deux
objets, que connaît l'hiérarchie Connect, et passer un pointeur
à cette struct ; alors, chaque supplantation de fonction
choisit l'objet qu'elle veut de la struct.

--
James Kanze GABI Software
Conseils en informatique orientée objet/
Beratung in objektorientierter Datenverarbeitung
9 place Sémard, 78210 St.-Cyr-l'École, France, +33 (0)1 30 23 00 34


Avatar
Sylvain
kanze wrote on 28/06/2006 11:41:
Sylvain wrote:
Michael wrote on 28/06/2006 02:47:

Malheureusement, foo1 et foo2 n'ont pas de base de commune,
je n'aurai pas du choisir des noms aussi proches...


il ne tient qu'à toi !


S'il en est auteur. Si les deux classes viennent de deux
bibliothèques tièrces différentes, en revanche...


?!?, ayant lib1::class1 et lib2::class2, je peux bien écrire:

class MyInterface {
public:
retType aService(...) = 0;
}

class MyClass{1|2} : public lib{1|2}::class{1|2}, public MyInterface {
....
public:
retType aService(...) { ... }
}

soit, je me taperais surement une palanquée de constructeurs à recopier,
mais il me semble de je peux quand même hériter d'une classe ... même si
je n'en suis pas l'auteur.


Et en quoi est-ce que ça résoud sa problème


ça permettait de coder un "getConnector" (une fonction faisant le boulot
de recherche de la donnée membre) une fois pour toute (c'est explicit
dans mon post 27/06 22:17).

Sylvain.