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

Problème classes abstraites ...

12 réponses
Avatar
Hamiral
Bonjour,

J'ai un ensemble de classes comme suit :

class Renderable {
friend class Renderer;

public:
Renderable() {};
virtual ~Renderable() {};
virtual Renderer* getRenderer() = 0;
}

class Renderer{
public:
Renderer() {};
virtual ~Renderer() {};
virtual void render(const Renderable* const renderable) = 0;
};

class Mesh : Renderable {
friend class MeshRenderer;

public:
Mesh() { ... };
~Mesh() { ... };
virtual MeshRenderer* getRenderer() { return new MeshRenderer; };
};

class MeshRenderer : public Renderer
{
public:
MeshRenderer() { ... };
virtual ~MeshRenderer() { ... };
virtual void render(Mesh* const mesh) { ... };
};

Et quand je compile cela, je me retrouve systématiquement avec les messages
d'erreur suivants :

/.../mesh.cpp: In member function 'virtual MeshRenderer*
Mesh::getRenderer()':
/.../mesh.cpp:28: error: cannot allocate an object of abstract
type 'MeshRenderer'
/.../meshrenderer.h:14: note: because the following virtual functions are
pure within 'MeshRenderer':
/.../renderer.h:17: note: virtual void Renderer::render(const Renderable*)

Ce que je ne comprends pas, c'est que la méthode Renderer::render(const
Renderable*) n'est PAS pure !!! Je ne comprends pas pourquoi le compilateur
la traite comme telle ?
Si, dans Renderer, je change la méthode virtuelle pure render() en non pure,
cela compile, mais ça ne correspond alors plus à mon design ... Seulement
je ne trouves pas où se trouve mon erreur ; quelqu'un peut m'aider ?

Merci infiniment par avance !
--
Hamiral

10 réponses

1 2
Avatar
Loïc Joly
Bonjour,
class Renderer{
virtual void render(const Renderable* const renderable) = 0;
};

class MeshRenderer : public Renderer
{
virtual void render(Mesh* const mesh) { ... };
};


Tu crois redéfinir la fonction, mais comme les paramètres sont
différents, tu définis juste une autre fonction portant le même nom.

--
Loïc

Avatar
Hamiral
Loïc Joly wrote:

Bonjour,
class Renderer{
virtual void render(const Renderable* const renderable) = 0;
};

class MeshRenderer : public Renderer
{
virtual void render(Mesh* const mesh) { ... };
};


Tu crois redéfinir la fonction, mais comme les paramètres sont
différents, tu définis juste une autre fonction portant le même nom.



Je pensais que, comme Mesh est une classe dérivée de Renderable, cela ne
posait pas de problème. Et justement, en remplaçant
MeshRenderer::render(Mesh* const) par MeshRenderer::render(Renderable*
const), j'ai toujours les mêmes messages d'erreur ...

--
Hamiral


Avatar
Loïc Joly
Loïc Joly wrote:



Bonjour,
class Renderer{
virtual void render(const Renderable* const renderable) = 0;
};

class MeshRenderer : public Renderer
{
virtual void render(Mesh* const mesh) { ... };
};


Tu crois redéfinir la fonction, mais comme les paramètres sont
différents, tu définis juste une autre fonction portant le même nom.




Je pensais que, comme Mesh est une classe dérivée de Renderable, cela ne
posait pas de problème.


Et si...

Et justement, en remplaçant
MeshRenderer::render(Mesh* const) par MeshRenderer::render(Renderable*
const), j'ai toujours les mêmes messages d'erreur ...



Il te manque toujours un const...

--
Loïc



Avatar
Hamiral
Loïc Joly wrote:

Il te manque toujours un const...



Oui, je m'en suis rendu compte après, mais même en l'ajoutant, ou en
enlevant tous les const, j'ai toujours le même message d'erreur ...

--
Hamiral

Avatar
Hamiral
Hamiral wrote:

Loïc Joly wrote:

Il te manque toujours un const...



Oui, je m'en suis rendu compte après, mais même en l'ajoutant, ou en
enlevant tous les const, j'ai toujours le même message d'erreur ...

Oups j'ai dit une ânerie.


Bon, j'ai enlevé les const, remplacé Mesh par Renderable là où il faut, et
ce message d'erreur a disparu ... Mais un autre l'a remplacé !
Au début de ma méthode MeshRenderer::render() ressemble à ça maintenant :

void MeshRenderer::render(Renderable* renderable) {
Mesh* mesh = renderable;
// ... suite du corps de la méthode, utilisant mesh
}

Seulement j'ai maintenant ce message d'erreur :
/.../meshrenderer.cpp:19: error: invalid conversion from 'Renderable*'
to 'Mesh*'

Or, ma classe Mesh dérive de Renderable ... Je présume donc qu'il me manque
un xxx_cast<...> quelque part, non ?

Merci encore pour votre aide :)

--
Hamiral


Avatar
Fabien LE LEZ
On Sat, 27 May 2006 17:27:15 +0200, Hamiral :

/.../meshrenderer.cpp:19: error: invalid conversion from 'Renderable*'
to 'Mesh*'

Or, ma classe Mesh dérive de Renderable ... Je présume donc qu'il me manque
un xxx_cast<...> quelque part, non ?


dynamic_cast<>.

Avatar
Loïc Joly

Seulement j'ai maintenant ce message d'erreur :
/.../meshrenderer.cpp:19: error: invalid conversion from 'Renderable*'
to 'Mesh*'

Or, ma classe Mesh dérive de Renderable ... Je présume donc qu'il me manque
un xxx_cast<...> quelque part, non ?


Oui, un
Mesh* mesh = dynamic_cast<Mesh*>(renderable);
if (mesh != NULL)
{ // ...
}

Mais souvent, c'est signe d'un design faible de devoir faire ça, puisque
ça couple les classes de rendu avec les classes concrètes que l'on peut
rendre. Es-tu certain qu'il n'y a pas moyen de travailler dans cette
fonction avec un Renderable*, sans savoir son type précis ?

--
Loïc

Avatar
Hamiral
Loïc Joly wrote:


Seulement j'ai maintenant ce message d'erreur :
/.../meshrenderer.cpp:19: error: invalid conversion from 'Renderable*'
to 'Mesh*'

Or, ma classe Mesh dérive de Renderable ... Je présume donc qu'il me
manque un xxx_cast<...> quelque part, non ?


Oui, un
Mesh* mesh = dynamic_cast<Mesh*>(renderable);
if (mesh != NULL)
{ // ...
}


Ok.

Mais souvent, c'est signe d'un design faible de devoir faire ça, puisque
ça couple les classes de rendu avec les classes concrètes que l'on peut
rendre. Es-tu certain qu'il n'y a pas moyen de travailler dans cette
fonction avec un Renderable*, sans savoir son type précis ?


Je pense effectivement que j'ai un problème de design. J'avais imaginé la
chose comme suit : j'ai une collection de Renderable, qui peuvent être, par
exemple, des Mesh, des Nurbs, des BezierPatch, ou je ne sais quoi d'autre.
Chaque sorte de Renderable a son Renderer associé, qui, lui, sait comment
on doit effectuer le rendu d'un Nurbs ou d'un BezierPatch par exemple.

Bien sûr il y a la solution "facile" pour laquelle render() est une méthode
de Renderable, donc chaque objet sait lui-même comment s'afficher, mais je
n'aime pas trop cela. Je voyais chaque Renderable comme transportant les
informations servant à effectuer son rendu, et chaque Renderer
correspondant sachant comment utiliser les informations du Renderable
associé dans le but d'en effectuer le rendu. Ce qui permettait
éventuellement de décentraliser le rendu. Par exemple, pour ce qui est du
MeshRenderer (qui serait un singleton dans ce cas), il pourrait simplement
récupérer tous les polygones des Mesh à afficher, puis, il aurait une
méthode flush() qui enverrait alors tous les polygones à la carte vidéo en
une seule fois, et de manière optimisée.

Voilà donc pourquoi j'avais opté pour un tel design, mais maintenant, après
votre remarque, je ne sais plus trop comment procéder ...

--
Hamiral


Avatar
tristan.carel
Hamiral wrote:
Loïc Joly wrote:


Seulement j'ai maintenant ce message d'erreur :
/.../meshrenderer.cpp:19: error: invalid conversion from 'Renderable*'
to 'Mesh*'

Or, ma classe Mesh dérive de Renderable ... Je présume donc qu'il me
manque un xxx_cast<...> quelque part, non ?


Oui, un
Mesh* mesh = dynamic_cast<Mesh*>(renderable);
if (mesh != NULL)
{ // ...
}


Ok.

Mais souvent, c'est signe d'un design faible de devoir faire ça, puis que
ça couple les classes de rendu avec les classes concrètes que l'on peut
rendre. Es-tu certain qu'il n'y a pas moyen de travailler dans cette
fonction avec un Renderable*, sans savoir son type précis ?



Lorsqu'il est possible de determiner le type a la compile, il vaut
mieux le faire. La solution que tu mets en evidence ne fait que
deplacer le probleme a l'execution. On est alors oblige de se paner des
dynamic_cast dans tous les sens avec le petit != 0 qui montre qu'il y a
des lacunes dans la modelisation.

Je pense effectivement que j'ai un problème de design. J'avais imagin é la
chose comme suit : j'ai une collection de Renderable, qui peuvent être, par
exemple, des Mesh, des Nurbs, des BezierPatch, ou je ne sais quoi d'autre.
Chaque sorte de Renderable a son Renderer associé, qui, lui, sait comme nt
on doit effectuer le rendu d'un Nurbs ou d'un BezierPatch par exemple.

Bien sûr il y a la solution "facile" pour laquelle render() est une m éthode
de Renderable, donc chaque objet sait lui-même comment s'afficher, mais je
n'aime pas trop cela. Je voyais chaque Renderable comme transportant les
informations servant à effectuer son rendu, et chaque Renderer
correspondant sachant comment utiliser les informations du Renderable
associé dans le but d'en effectuer le rendu. Ce qui permettait
éventuellement de décentraliser le rendu.


C'est tout a ton honneur de vouloir opter pour un tel choix, tout
blinder dans une meme classe diminue la lisibilite et la maintenabilite
du code. D'autant que tu ne comptes certainement pas avoir uniquement
une seule operation sur ces donnees, ce qui signifie que d'autres
methodes pourraient s'y ajouter.


Resume de la situation:
1. Tu souhaites separer les donnees des operations qu'il est possible
d'effectuer dessus
2. Tu disposes d'une hierarchie de classes pour tes donnees (bon ok
d'un seul niveau mais hierarchie quand meme)

==> Le design-pattern `visitor' est la solution a tous tes problemes:
http://www-eleves-isia.cma.fr/documentation/DesignPatterns/pat5kfso.htm

Il faut diviser pour regner!

Par exemple, pour ce qui est du
MeshRenderer (qui serait un singleton dans ce cas), il pourrait simplement
récupérer tous les polygones des Mesh à afficher, puis, il aurait u ne
méthode flush() qui enverrait alors tous les polygones à la carte vid éo en
une seule fois, et de manière optimisée.

Voilà donc pourquoi j'avais opté pour un tel design, mais maintenant, après
votre remarque, je ne sais plus trop comment procéder ...


voyons un petit exemple permettant, je pense, de resoudre ton
probleme:


#include <iostream>

// Forward declarations
class Mesh;
class Nurbs;
class Renderable;


/** Declaration d'un objet fonction dont le but est de rendre des
objets
** `Renderable'.
*/
class Renderer
{
public:
void operator () (const Renderable&);
void operator () (const Mesh&);
void operator () (const Nurbs&);
};


/*------------------------------------.
| Declaration des objets `Renderable' |
`------------------------------------*/

class Renderable
{
public:
virtual ~Renderable() {};
virtual void accept(Renderer&) const = 0;
};

class Mesh : public Renderable
{
public:
virtual void accept(Renderer& e) const { e (*this);}
};

class Nurbs : public Renderable
{
public:
virtual void accept(Renderer& e) const { e (*this);}
};


/*---------------------------------------------------------.
| Methodes dont le but est d'effectuer le rendu de l'objet |
| `renderable' passe en parametre |
`---------------------------------------------------------*/


/** Utilisation du type dynamique pour retrouver la bonne methode
`render'.
**
** Appel de la methode accept de l'objet qui va relance le visiteur
sur
** lui-meme.
*/
void
Renderer::operator() (const Renderable& e)
{
e.accept (*this);
}

void
Renderer::operator () (const Mesh& e)
{
// On a acces en lecture a notre Mesh!!!
std::cout << "visiting a Mesh" << std::endl;
}

void
Renderer::operator() (const Nurbs& e)
{
// On a acces en lecture a notre Nurbs!!!
std::cout << "visiting a Nurbs" << std::endl;
}



Le code ci-dessus est agence de facon a compiler dans un seul fichier.
Mais il n'y a aucun probleme pour separer l'objet fonctoin `Renderer'
des `Renderable' modulo quelques forward declarations ;)

Voyons ce que cela donne avec un petit main:

int main ()
{
Renderer render;
Nurbs n;
Mesh m;
Renderable* n2 = new Nurbs ();

render (n);
render (m);
render (*n2);
delete (n2);
}

$ ./a.out
visiting a Nurbs
visiting a Mesh
visiting a Nurbs
$

--
Tristan Carel
http://wcube.epita.fr/~tristan



Avatar
Hamiral
wrote:
Le code ci-dessus est agence de facon a compiler dans un seul fichier.
Mais il n'y a aucun probleme pour separer l'objet fonctoin `Renderer'
des `Renderable' modulo quelques forward declarations ;)


Merci beaucoup, je pense que ça va m'aider à me débloquer dans mon
développement :)

--
Hamiral

1 2