1

J'ai voulu faire un peu la même chose qu'avec des interfaces en C++, par exemple si je simplifie le modèle et enlève les getters: // Indique un objet rectangulaire struct HasFrame { void setPosition(Vector pos); void setSize(Vector size); }; // La rotation n'a pas de sens sans un centre, inscrit dans une zone rectangulaire struct HasRotatableFrame: public HasFrame { void setCenter(Vector center); void setAngle(float angle); }; // Si un objet a une texture, il faut aussi qu'il soit rectangulaire, donc ait un cadre struct HasRectangularTexture: public HasFrame { // Met aussi à jour le cadre pour que le bout d'image sélectionné soit dessiné à 1:1 void setSourceRect(Vector start, Vector end); }; struct Image: public HasRotatableFrame, public HasRectangularTexture { void draw() { setPosition(Vector(0, 0)); } };

Mais ça n'est pas possible... on obtient ceci sur l'appel setPosition de la méthode draw():
error C2385: ambiguous access of 'setPosition'
          could be the 'setPosition' in base 'HasFrame'
          or could be the 'setPosition' in base 'HasFrame'

La question c'est que mon design est sûrement mauvais pour du C++, et vu qu'il y a des bons ici, peut être pourrez vous me conseiller sur la manière dont il faudrait s'y prendre dans ce langage? smile
Merci d'avance ^^
avatar
Highway Runners, mon jeu de racing à la Outrun qu'il est sorti le 14 décembre 2016 ! N'hésitez pas à me soutenir :)

https://itunes.apple.com/us/app/highway-runners/id964932741

2

Alors bon une manière que j'ai trouvé de faire, c'est un peu dégueulasse et difficile à documenter proprement (voire impossible), c'est d'utiliser un template plutôt que forcer l'existence d'un cadre par héritage. Par exemple:
template <typename FrameType> struct HasRectangularTexture: public FrameType { // ici on peut utiliser setPosition par exemple // pour peu que FrameType soit bien une sous classe de HasFrame... // mais pas moyen de le montrer explicitement }; struct Image: public HasRectangularTexture<HasRotatableFrame> { ... };
Kess vous en pensez?
avatar
Highway Runners, mon jeu de racing à la Outrun qu'il est sorti le 14 décembre 2016 ! N'hésitez pas à me soutenir :)

https://itunes.apple.com/us/app/highway-runners/id964932741

3

Bah, il y a 2 solutions: soit tu appelles explicitement HasRotatableFrame::setPosition (ça contourne le problème, mais tu auras toujours 2 copies de HasFrame dans ta classe), soit tu utilises l'héritage virtuel (la solution la plus propre, mais ça a aussi ses désavantages, notamment qu'il faut des dynamic_cast à certains endroits où normalement un static_cast suffirait).
avatar
Mes news pour calculatrices TI: Ti-Gen
Mes projets PC pour calculatrices TI: TIGCC, CalcForge (CalcForgeLP, Emu-TIGCC)
Mes chans IRC: #tigcc et #inspired sur irc.freequest.net (UTF-8)

Liberté, Égalité, Fraternité

4

L'héritage virtuel! Voilà, je pense, ce qui me manquait.
Compte tenu que je souhaite surtout de la vitesse, je pense que je vais généraliser et éliminer par exemple le type HasRotatableFrame. I.e. un objet qui 'HasFrame' peut aussi supporter la rotation par défaut, comme ça ça me rajoute un if (angle != 0) faitLaRotation(); mais c'est relativement ridicule par rapport au coût de branchement d'avoir tout virtuel. Enfin j'imagine, je sais pas trop?
avatar
Highway Runners, mon jeu de racing à la Outrun qu'il est sorti le 14 décembre 2016 ! N'hésitez pas à me soutenir :)

https://itunes.apple.com/us/app/highway-runners/id964932741

5

Je te conseille d'essayer de comprendre avant tout comment ça fonctionne: http://en.wikipedia.org/wiki/Virtual_inheritance
avatar
Mes news pour calculatrices TI: Ti-Gen
Mes projets PC pour calculatrices TI: TIGCC, CalcForge (CalcForgeLP, Emu-TIGCC)
Mes chans IRC: #tigcc et #inspired sur irc.freequest.net (UTF-8)

Liberté, Égalité, Fraternité

6

Merci ^^
Kevin Kofler (./3) :
soit tu utilises l'héritage virtuel (la solution la plus propre, mais ça a aussi ses désavantages, notamment qu'il faut des dynamic_cast à certains endroits où normalement un static_cast suffirait).

En fait justement je ne connais que la base, et c'est ta phrase qui m'a fait peur. Si tu as envie d'expliciter, ce serait volontiers. Dans quel cas par exemple on aurait ce genre de problème?
avatar
Highway Runners, mon jeu de racing à la Outrun qu'il est sorti le 14 décembre 2016 ! N'hésitez pas à me soutenir :)

https://itunes.apple.com/us/app/highway-runners/id964932741

7

Tu as plus de détails là: http://www.parashift.com/c++-faq-lite/multiple-inheritance.html#faq-25.8 (et successives) et dans cet article: http://www.drdobbs.com/184402074.

Pour l'histoire des dynamic_cast, si tu as un diamant de la sorte:
class A : public B, public C { … };
class B : virtual public D { … };
class C : virtual public D { … };
class D { … };

alors tu vas avoir 2 problèmes avec static_cast:
1. Un static_cast de D* en B* ou C* est autorisé, mais si tu as un D* qui est en réalité un A* et si tu fais un static_cast vers B* ou C*, ça va foirer!
2. Un static_cast de D* en A* n'est carrément pas autorisé!
Tout ceci parce qu'il y a un offset à respecter lors de la conversion contrairement à l'héritage simple et parce que cet offset est de plus variable contrairement à l'héritage multiple non-virtuel. Seule la machinerie RTTI permet de calculer le bon offset en fonction du type réel de l'objet.
(Et évidemment, reinterpret_cast est carrément inutilisable pour ces cas, il foire même pour l'héritage multiple non-virtuel parce qu'il ne gère pas les offsets.)
avatar
Mes news pour calculatrices TI: Ti-Gen
Mes projets PC pour calculatrices TI: TIGCC, CalcForge (CalcForgeLP, Emu-TIGCC)
Mes chans IRC: #tigcc et #inspired sur irc.freequest.net (UTF-8)

Liberté, Égalité, Fraternité

8

Merci bcp smile
Sinon dans le même genre, mettons que j'aie: class Textured {}; class TexturedTiled : public Textured {}; class Image : public Textured {}; class Sprite : public Image, public TexturedTiled {};
En gros le Sprite est une Image mais qui utilise la classe TexturedTiled au lieu de Textured, mais ça ne m'a pas l'air viable comme ça. L'héritage virtuel pourrait-il m'aider ici?
avatar
Highway Runners, mon jeu de racing à la Outrun qu'il est sorti le 14 décembre 2016 ! N'hésitez pas à me soutenir :)

https://itunes.apple.com/us/app/highway-runners/id964932741

9

Oui, c'est là aussi un héritage en diamant, donc là aussi l'héritage virtuel est une solution pour s'en sortir.
avatar
Mes news pour calculatrices TI: Ti-Gen
Mes projets PC pour calculatrices TI: TIGCC, CalcForge (CalcForgeLP, Emu-TIGCC)
Mes chans IRC: #tigcc et #inspired sur irc.freequest.net (UTF-8)

Liberté, Égalité, Fraternité

10

Oui, tu dois utiliser l'héritage virtuel pour ne pas qu'un objet sprite contienne deux objets Textured : class Textured {}; class TexturedTiled : virtual public Textured {}; class Image : virtual public Textured {}; class Sprite : public Image, public TexturedTiled {};
Et tu dois prévoir un constructeur sans argument pour Textured, parce qu'en C++, tu ne pourras pas lui passer de paramètres.

11

Euh si, on peut utiliser un member initializer pour passer des paramètres au constructeur de la classe dont on dérive. Hmmm, peut-être pas ici, parce que lequel des member initializers l'emporterait?

EDIT: Bon, j'ai relu http://www.parashift.com/c++-faq-lite/multiple-inheritance.html#faq-25.12 et le verdict est: oui, on peut utiliser un member initializer. Mais, et il y a un gros "mais", un seul de ces appels au constructeur sera effectivement effectué: celui à partir de la classe "la plus dérivée", et du coup cette classe "la plus dérivée" doit avoir le bon member initializer pour la base virtuelle même quand elle ne l'utilise pas directement. sick Bref, il faut faire très attention à ce qu'on fait. Donc du coup le conseil de Folco (prévoir un constructeur sans paramètres) n'est peut-être pas aussi mauvais que ça. smile
avatar
Mes news pour calculatrices TI: Ti-Gen
Mes projets PC pour calculatrices TI: TIGCC, CalcForge (CalcForgeLP, Emu-TIGCC)
Mes chans IRC: #tigcc et #inspired sur irc.freequest.net (UTF-8)

Liberté, Égalité, Fraternité