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

Problème de design

16 réponses
Avatar
FR C++
Bonjour =E0 tous,

comme le titre l'indique, j'ai un souci de design de classe.

J'ai cr=E9=E9 trois classes qui me permettent d'afficher de la vid=E9o
gr=E2ce =E0 DirectShow.

J'ai une classe DS_InfosVideo, DS_AffichVideo, qui sont assez
explicites, et une derni=E8re classe TVideoAffich qui est la fen=EAtre
qui affichera la vid=E9o, et qui a comme membres priv=E9s les deux
classes pr=E9c=E9dentes...

Je peux afficher 3 types de vid=E9o: les fichiers vid=E9o, des fichiers
XML pour la pr=E9visualisation de montages vid=E9o, et de la vid=E9o en
temps-r=E9el provenant d'une cam=E9ra num=E9rique.

J'ai donc utilis=E9 l'idiome du constructeur nomm=E9 pour chacune de ces
3 classes, ce qui me permet de charger les bonnes infos.

Par exemple, pour un fichier vid=E9o:

class TVideoAffich
{
private:
DS_InfosVideo * Infos;
DS_AffichVideo * Affich;
public:
static TVideoAffich * CreateFromFile(const std::string & path)
{
Infos =3D DS_InfosVideo::CReateFromFile(path);
Affich =3D DS_AffichVideo::CReateFromFile(path);
}
};

Pour l'instant, je limite le rendu de la vid=E9o =E0 un renderer que j'ai
choisi.

Ce que j'aimerai faire maintenant, c'est proposer d'autres choix de
renderer.

Mais je ne me vois pas faire des fonctions du genre pour chacune des
classes:

static TVideoAffich * CreateFromFileVMRRenderer(const std::string &
path);
static TVideoAffich * CreateFromFileBasicRenderer(const std::string &
path);
static TVideoAffich * CreateFromSBEVMRRenderer(const std::string &
path);

etc...

Je voyais bien un truc du genre:

template <class Input>
class DS_InfosVideo : public Input
{
public:
DS_InfosVideo(const std::string & path);
};

template <class Input , class Renderer>
class TVideoAffich : public Input, public Renderer
{
private:
DS_InfosVideo<Input> * Infos;
DS_AffichVideo<Input,Renderer> * Affich;
public:
TVideoAffich(const std::string & path);
};

Seulement la classe Input pass=E9e =E0 TVideoAffich n'est pas la m=EAme
que celle que doit recevoir DS_InfosVideo ni celle de DS_AffichVideo...

Est-ce que c'est possible de poser des conditions =E0 la compilation, de
telle sorte qu'on ait un truc du genre:

pour TVideoAffich
si Input =3D=3D VideoAffichInputFile
{
DS_InfosVideo<Infos_Input_File> * Infos;
si Renderer =3D=3D VideoAffichVMRRenderer
DS_AffichVideo<Affich_Input_File,Affich_VMR_Renderer> * Affich;
sinon
DS_AffichVideo<Affich_Input_File,Affich_Basic_Renderer> *
Affich;
}
sinon
{
///
}

Voil=E0 tout :)

Merci d'avance

Mike

10 réponses

1 2
Avatar
Fabien LE LEZ
On 16 Jun 2006 05:40:38 -0700,

"FR C++"


Problème logiciel ? Ton nom est passé à la trappe :-(

:

J'ai créé trois classes qui me permettent d'afficher de la vidéo
grâce à DirectShow.


Tel que je comprends le truc, tu as trois types de classe :

- une classe "Source" (dans laquelle on va chercher la vidéo)
- une classe "Destination" (on lui envoie les pixels, et elle s'occupe
de les afficher à l'écran)
- une classe qui chapeaute le tout.

La solution me paraît évidente (mais il est possible que j'aie oublié
un détail) : deux hiérarchies de classes.

class Source
{
public:
virtual Pixels GetPixels() const= 0;
};

class Source_FichierAvi: public Source
{
public:
virtual Pixels GetPixels() const; // implémentation
};

class Destination
{
public:
virtual void EnvoyerPixels (Pixels const&)= 0;
};

class Destination_Renderer1: public Destination
etc.

La classe "Chapeaute" aura alors un membre Source* (pointeur vers un
Source_quelque_chose) et un membre Destination*.

Avatar
Michael
En fait ce n'est pas tout à fait ça...

La classe Infos se charge de récupérer les caractéristiques de la
source (largeur, hauteur, framerate, etc...), avec des fonctions
différentes selon la source.

La classe Affich se charge de connecter une source à une destination,
mais la manière de traiter la source est différente de la classe
Infos.

La classe qui chapeaute le tout se sert de Infos pour la taille de la
fenêtre, et de Affich pour afficher la vidéo sur cette même
fenêtre...

Ce que je veux, c'est pouvoir définir pour la classe qui chapeaute le
tout la manière dont Infos et Affich vont s'occuper de la source et de
l'affichage...

Donc:

VideoAffich<Basic,Fichier> t; entrainerait Infos<Fichier> et
Affich<Basic,Fichier>..

J'ai essayé de m'en sortir avec de la templatisation partielle:

class Infos_InputFile {}
class Affich_InputFile {}
class Affich_AffichVMR {}
class Affich_AffichBasic {}

class TVA_InputFichier {}
class TVA_InputXML {}

class TVA_RenderVMR {}
class TVA_RenderBasic {}

template <class Input , class Renderer>
class TVideoAffich
{
private:
typedef Infos<Input> infos;
typedef Affich<Input,Render> affich;

infos i;
affich a:
public:
TVideoAffich();

void SetPosition(const int position) { // }
};

et en spécialisant pour chacune des solutions possibles

template <>
class TVideoAffich<TVA_InputFile,TVA_RenderBasic>
{
private:
typedef Infos<Infos_InputFile> infos;
typedef Affich<Affich_InputFile,Affich_RenderBasic> affich;

infos i;
affich a:
public:
TVideoAffich();
};

Mais si je fais par exemple:

typedef TVideoAffich<TVA_InputFile,TVA_RenderBasic> toto;
toto t;
t.SetPOsition(8);

ça ne marche pas parce que SetPOsition n'est pas définie pour la
spécialisation utilisée...

Sinon, plutôt que de passer une classe comme paramètre template, je
pensais passer un entier issu d'une énumération, et en fonction de la
valeur de cette enum utiliser derrière les bons template...

du genre:

enum InputType { File , XML , XBE };
enum RendererType { VMR , Basic };

template <InputType input, RendererType render>
class TVideoAffich
{
private:
typedef Infos<input> infos;
typedef Affich<input,render> affich;

infos i;
affich a;
public:
//.....
};

Et pour Infos par exemple:

template<int Input>
class Infos
{

};

Et là je bloque: comment changer le comportement de Infos en fonction
de la valeur de l'énumération, et de préférence à la compilation?

La spécialisation fonctionne comme ça:

template<>
class Infos<0>
{
//File
};

template<>
class Infos<1>
{
//XML
};

Après, peut-être tout simplement que les template ne sont pas la
solution, mais je ne vois pas comment faire dans ce cas...

J''espère avoir été clair ;)
Avatar
Sylvain Togni
Après, peut-être tout simplement que les template ne sont pas la
solution, mais je ne vois pas comment faire dans ce cas...


Il y a deux principaux types de polymorphisme :

- Le polymorphisme statique (templates, surchage, ...).
Le comportement est choisi *pendant la compilation* et
reste donc fixe pendant l'exécution.
- Le polymorphisme dynamique (fonctions virtuelles, ...)
Le comportement est choisi *pendant l'exécution*.

Je n'ai pas tout compris, mais il me semble que c'est plutôt
le second qui répond à tes besoins.

--
Sylvain Togni

Avatar
Fabien LE LEZ
On 16 Jun 2006 08:24:48 -0700, "Michael" :

La classe Infos se charge de récupérer les caractéristiques de la
source (largeur, hauteur, framerate, etc...), avec des fonctions
différentes selon la source.


Si les fonctions ont des noms différents mais des fonctionnalités
identiques (ou suffisamment proches), il faut créer un adaptateur.

Tu commences par définir une interface pour la classe abstraite
Source :

class Source
{
public:
Taille GetTailleImage() const= 0;
};

Ensuite, tu l'implémentes, en appelant les fonctions qui vont bien.
J'ai pris l'exemple de deux classes, FichierAVI et FichierXML issues
de deux bibliothèques (fictives) différentes, et ayant des façons
différentes de donner la taille de l'image :


class Source_Avi: public Source
{
public:
Taille GetTailleImage() const
{
return Taille (fichier_avi.GetLargeur(),
fichier_avi.GetHauteur());
}

private:
FichierAVI fichier_avi;
};

class Source_XML: public Source
{
public:
Taille GetTailleImage() const
{
Taille reponse;
if (fichier_xml.ObtenirTaille (reponse))
{
return reponse;
}
else
{
throw quelque_chose;
}
}

private:
FichierXML fichier_xml;
};

Avatar
Fabien LE LEZ
On 16 Jun 2006 08:24:48 -0700, "Michael" :

Après, peut-être tout simplement que les template ne sont pas la
solution


C'était implicite dans le message auquel tu réponds
(<news:).

Avatar
Fabien LE LEZ
On 16 Jun 2006 08:24:48 -0700, "Michael" :

Après, peut-être tout simplement que les template ne sont pas la
solution


Les templates peuvent éventuellement remplacer le polymorphisme
d'héritage (même si ça ne me paraît pas conseillé dans cette situation
précise).
Par contre, il me semble que les spécialisations de templates n'ont
rien à faire ici.

Avatar
Michael
J'ai bien sûr déjà pensé à l'héritage, mais ça me pose un
problème:

pour le moment j'ai uniquement le paramètre Input qui varie, parce que
j'ai un seul Renderer défini.

Donc j'ai les classes:

class InfosVideo;
class InfosVideo_XML : public : InfosVideo;
class InfosVideo_FileL : public : InfosVideo;
class InfosVideo_SBE : public : InfosVideo;

class AffichVideo;
class AffichVideo_XML : public : AffichVideo;
class AffichVideo_FileL : public : AffichVideo;
class AffichVideo_SBE : public : AffichVideo;

ce qui ne me pose pas vraiment de soucis.

Mais je compte proposer un renderer de plus. Ca ne change rien aux
classes InfosVideo et dérivées, mais du coup ça multiplie le nombre
de classes AffichVideo par 2!

class AffichVideo_XML_VMR : public : AffichVideo;
class AffichVideo_XML_Overlay : public : AffichVideo;
etc...

Et donc si on ajoute encore des options, ça devient vite ingérable...

J'avais donc pensé aux templates pour faire quelque chose comme les
policies.

On a une classe de départ, et on change 2 paramètres template pour
définir de quel type est le fichier d'entrée, et grâce à quel
renderer il va s'afficher.

On passe donc ces deux paramètres à la classe qui englobe InfosVideo
et AffichVideo, qui elle se charge derrière de les instancier avec les
bons paramètres template, propres à chacune. (Le paramètre Input de
AffichVideo n'est pas le même que celui de InfosVideo)...

Il serait possible je pense de faire un truc du genre:

template <class InputTypeInfos , class InputTypeAffich, class
RenderType>
class Global
{
private:
Infos<InputTypeInfos> i;
Affich<InputTypeAffich,RenderType> a;
//...
}

Mais j'aimerai éviter la redondance entre InputTypeInfos et
InputTypeAffich qui indiquent tous les deux qu'il s'agit de le même
entrée...

J'avoue ne pas vraiment voir de solution à base de template, sûrement
parce que je ne maitrise pas encore vraiment.

Mais une solution à base de polymorphisme me semble compliquée
également...
Avatar
Jean-Marc Bourguet
"Michael" writes:

Mais une solution à base de polymorphisme me semble compliquée
également...


Si je comprends bien ton problème, tu oublies que
l'interface de InfosVideo doit suffire à un descendant de
AffichVideo pour fonctioner. Donc il y a simplement autant
de descendants de AffichVideo qu'il y a de renderer, autant
de descendants de InfosVideo qu'il y a de format de fichier.

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
Fabien LE LEZ
On 16 Jun 2006 12:08:43 -0700, "Michael" :

class AffichVideo_XML_VMR : public : AffichVideo;
class AffichVideo_XML_Overlay : public : AffichVideo;


Il y a à mon avis une erreur ici : tu as mélangé deux classes dans une
seule.
"XML", c'est la source.
"VMR" ou "Overlay", c'est la destination.

J'ai l'impression que tu as séparé deux éléments de "source" (i.e. la
lecture des pixels proprement dite, et la lecture des caractéristiques
de la vidéo, comme la taille), tout en mélangeant la source et la
destination des pixels dans la même classe.

C'est pour ça que je te conseille d'avoir deux hiérarchies séparées,
une regroupant tout ce qui concerne la source, et l'autre regroupant
tout ce qui concerne la destination.

Ainsi, l'objet "Chapeau" sait qu'il a une source, qui lui donne des
pixels et des renseignements sur ces pixels, et une destination, qui
attend des pixels et quelques autres informations.
À aucun moment, l'objet "Chapeau" ne doit savoir ce que signifie "XML"
ou "Overlay" -- c'est pas son problème.

De plus, comme n'importe quelle source peut être mise en rapport avec
n'importe quelle destination (sans que la destination connaisse les
spécificités de la source), les types (Taille, Pixel, etc.) doivent
être unifiés. Du coup, l'utilisation de templates ne se justifie pas.

Avatar
Michael
J'ai du mal m'exprimer alors...

InfosVideo n'est pas une interface qu'on peut assimiler à une
source... Elle utilise des interfaces COM uniquement dédiées à la
récupération des infos d'une source vidéo.

Par contre AffichVideo est composée de deux "sous-objets": une source
(dont les interfaces COM sont différentes selon le type de la source :
XML, Fichier...) et un renderer, qui, peut-importe la source affiche le
rendu de cette source; Ce renderer peut lui aussi être de différents
types (VMR, Overlay, etc...)

Donc j'ai bien de multiples interfaces à gérer : Nbre sources dispos
* Nbre renderer dispos...
1 2