Je reprends un code C++ (sur Windows avec MFC) que je dois étendre. En
particulier je suis tombé sur un truc qui ne me semble pas très propre,
et je voudrais savoir comment l'améliorer.
Il y a une classe de base, mettons CBaseUI, qui est dérivée plusieurs
fois. Chacune des classe dérivées dérive aussi d'une sous-classe du CWnd
des Microsoft Fondation Classes :
class CTextUI : public CBaseUI, public CEdit;
class CPasswordUI : public CBaseUI, public CEdit;
class CEnumUI : public CBaseUI, public CComboBox;
class CDateUI : public CBaseUI, public CDateTimeCtrl;
etc.
Toutes les classes CEdit, CComboBox, CDateTimeCtrl, CButton, etc., sont
dérivées de CWnd, ce qui fait que les classes dérivées de CBaseUI sont
elles-mêmes dérivées de CWnd -- mais pas CBaseUI elle-même.
Le besoin est, à partir d'un pointeur CBaseUI * pointant vers l'une des
sous-classes, d'appeler la méthode GetWindowRect (définie dans CWnd).
l'implémentation actuelle passe par une fonction virtuelle dans CBaseUI,
définie dans chacune des sous-classes par :
void CTextUI::GetWindowRect(CRect *rect) // idem CPasswordUI, etc.
{
((CWnd *)this)->GetWindowRect(rect);
}
Ma question principale est : y a-t-il un moyen pour éviter de redéfinir
GetWindowRect vingt fois, avec vingt fois le même code ? J'ai essayé de
dériver CBaseUI de CWnd, mais alors j'obtiens des tas d'erreurs de
compilation « CTextUI::uneméthode is ambiguous ».
Par ailleurs, sans rien changer d'autre, puis-je au moins remplacer :
((CWnd *)this)->GetWindowRect(rect);
par :
CWnd::GetWindowRect(rect);
dans chaque classe dérivée ?
Cordialement,
--
Olivier Miakinen
Troll du plus sage chez les conviviaux : le nouveau venu, avec
son clan, s'infiltre dans les groupes de nouvelles. (3 c.)
merci d'avoir corrigé James c'est bien dynamic_cast que je voulais écrire - va savoir pourquoi j'ai tapé reinterpret ... mis à part 40° depuis 4 jours.
merci d'avoir corrigé James c'est bien dynamic_cast que je voulais
écrire - va savoir pourquoi j'ai tapé reinterpret ... mis à part 40°
depuis 4 jours.
merci d'avoir corrigé James c'est bien dynamic_cast que je voulais écrire - va savoir pourquoi j'ai tapé reinterpret ... mis à part 40° depuis 4 jours.
Sylvain.
Olivier Miakinen
Bonjour, et pardon d'avoir mis si longtemps à répondre.
c'est pas tant sur le polymorphisme que sur les contradictions et erreurs des MFC dans ce cas.
;-)
C'est bien possible, mais je n'ai pas les moyens d'y changer grand chose alors je dois faire avec (il n'est bien évidemment pas question que je remplace les MFC par autre chose dans ce projet qui contient près de 2000 fichiers).
Le besoin est, à partir d'un pointeur CBaseUI * pointant vers l'une des sous-classes, d'appeler la méthode GetWindowRect (définie dans CWnd).
ben y-a-qu'à alors !
la virtualité est impossible mais un cast vers une classe fille par voies détournées est possible:
J'ai bien noté que tu voulais écrire dynamic_cast à la place de reinterpret_cast, mais finalement j'ai opté pour la proposition de Jean-Marc Bourguet.
pour clarifier ce qui est commun à tous les composants visuels, vous pouvez commencer par introduire un HWND (disons hWnd) dans la classe CBaseUI en le mettant à null dans le constructeur.
puis prévoyez un méthode protégée telle:
HWND CBaseUI::getHandle(){ if (hWnd == null){ CWnd* wnd = reinterpret_cast<CWnd*>(this); if (wnd) hWnd = wnd->GetSafeHwnd(); } return hWnd; } (la création du handle graphique est toujours différée par rapport au constructeur - c'est fait dans ::CreateWindow[Ex])
Je ne connaissais pas ce handle. Merci de m'en avoir parlé : je ne m'en servirai probablement pas ici, mais cela pourrait m'être utile ailleurs.
Par ailleurs, sans rien changer d'autre, puis-je au moins remplacer : ((CWnd *)this)->GetWindowRect(rect); par : CWnd::GetWindowRect(rect); dans chaque classe dérivée ?
non, GetWindowRect n'est pas une méthode de classe mais d'instance, qui utilise la donnée membre publique (ben si pourquoi?) 'm_hWnd' de CWnd pour appeler la méthode globale comme nous l'avons fait ci-avant.
Je ne comprends pas ce qui gêne, là. Le CTextUI::GetWindowRect() et les autres sont aussi des méthodes d'instance, et il me semblait que l'instance était passée automatiquement dans ce cas là.
-- Olivier Miakinen Troll du plus sage chez les conviviaux : le nouveau venu, avec son clan, s'infiltre dans les groupes de nouvelles. (3 c.)
Bonjour, et pardon d'avoir mis si longtemps à répondre.
c'est pas tant sur le polymorphisme que sur les contradictions et
erreurs des MFC dans ce cas.
;-)
C'est bien possible, mais je n'ai pas les moyens d'y changer grand chose
alors je dois faire avec (il n'est bien évidemment pas question que je
remplace les MFC par autre chose dans ce projet qui contient près de
2000 fichiers).
Le besoin est, à partir d'un pointeur CBaseUI * pointant vers l'une des
sous-classes, d'appeler la méthode GetWindowRect (définie dans CWnd).
ben y-a-qu'à alors !
la virtualité est impossible mais un cast vers une classe fille par
voies détournées est possible:
J'ai bien noté que tu voulais écrire dynamic_cast à la place de
reinterpret_cast, mais finalement j'ai opté pour la proposition de
Jean-Marc Bourguet.
pour clarifier ce qui est commun à tous les composants visuels, vous
pouvez commencer par introduire un HWND (disons hWnd) dans la classe
CBaseUI en le mettant à null dans le constructeur.
puis prévoyez un méthode protégée telle:
HWND CBaseUI::getHandle(){
if (hWnd == null){
CWnd* wnd = reinterpret_cast<CWnd*>(this);
if (wnd)
hWnd = wnd->GetSafeHwnd();
}
return hWnd;
}
(la création du handle graphique est toujours différée par rapport au
constructeur - c'est fait dans ::CreateWindow[Ex])
Je ne connaissais pas ce handle. Merci de m'en avoir parlé : je ne m'en
servirai probablement pas ici, mais cela pourrait m'être utile ailleurs.
Par ailleurs, sans rien changer d'autre, puis-je au moins remplacer :
((CWnd *)this)->GetWindowRect(rect);
par :
CWnd::GetWindowRect(rect);
dans chaque classe dérivée ?
non, GetWindowRect n'est pas une méthode de classe mais d'instance, qui
utilise la donnée membre publique (ben si pourquoi?) 'm_hWnd' de CWnd
pour appeler la méthode globale comme nous l'avons fait ci-avant.
Je ne comprends pas ce qui gêne, là. Le CTextUI::GetWindowRect() et
les autres sont aussi des méthodes d'instance, et il me semblait que
l'instance était passée automatiquement dans ce cas là.
--
Olivier Miakinen
Troll du plus sage chez les conviviaux : le nouveau venu, avec
son clan, s'infiltre dans les groupes de nouvelles. (3 c.)
Bonjour, et pardon d'avoir mis si longtemps à répondre.
c'est pas tant sur le polymorphisme que sur les contradictions et erreurs des MFC dans ce cas.
;-)
C'est bien possible, mais je n'ai pas les moyens d'y changer grand chose alors je dois faire avec (il n'est bien évidemment pas question que je remplace les MFC par autre chose dans ce projet qui contient près de 2000 fichiers).
Le besoin est, à partir d'un pointeur CBaseUI * pointant vers l'une des sous-classes, d'appeler la méthode GetWindowRect (définie dans CWnd).
ben y-a-qu'à alors !
la virtualité est impossible mais un cast vers une classe fille par voies détournées est possible:
J'ai bien noté que tu voulais écrire dynamic_cast à la place de reinterpret_cast, mais finalement j'ai opté pour la proposition de Jean-Marc Bourguet.
pour clarifier ce qui est commun à tous les composants visuels, vous pouvez commencer par introduire un HWND (disons hWnd) dans la classe CBaseUI en le mettant à null dans le constructeur.
puis prévoyez un méthode protégée telle:
HWND CBaseUI::getHandle(){ if (hWnd == null){ CWnd* wnd = reinterpret_cast<CWnd*>(this); if (wnd) hWnd = wnd->GetSafeHwnd(); } return hWnd; } (la création du handle graphique est toujours différée par rapport au constructeur - c'est fait dans ::CreateWindow[Ex])
Je ne connaissais pas ce handle. Merci de m'en avoir parlé : je ne m'en servirai probablement pas ici, mais cela pourrait m'être utile ailleurs.
Par ailleurs, sans rien changer d'autre, puis-je au moins remplacer : ((CWnd *)this)->GetWindowRect(rect); par : CWnd::GetWindowRect(rect); dans chaque classe dérivée ?
non, GetWindowRect n'est pas une méthode de classe mais d'instance, qui utilise la donnée membre publique (ben si pourquoi?) 'm_hWnd' de CWnd pour appeler la méthode globale comme nous l'avons fait ci-avant.
Je ne comprends pas ce qui gêne, là. Le CTextUI::GetWindowRect() et les autres sont aussi des méthodes d'instance, et il me semblait que l'instance était passée automatiquement dans ce cas là.
-- Olivier Miakinen Troll du plus sage chez les conviviaux : le nouveau venu, avec son clan, s'infiltre dans les groupes de nouvelles. (3 c.)
Olivier Miakinen
Ma question principale est : y a-t-il un moyen pour éviter de redéfinir GetWindowRect vingt fois, avec vingt fois le même code ?
Une solution est de faire redéfinir 20 fois la fonction par un template:
template<class T> class CTBaseUI<T> : public T { public: void GetWindowRect(CRect* rect) { T::GetWindowRect(rect); } }
class CTextUI : public CTBaseUI<CEdit> { ... } class CPasswordUI : public CTBaseUI<CEdit> { ... } class CEnumUI : public CTBaseUI<CComboBox> { ... } class CDateUI : public CTBaseUI<CDateTimeCtrl> { ... }
Avec cette solution, est-il toujours possible d'avoir des méthodes spécifiques à chaque classe ? Parce que bien évidemment je ne fais pas le même traitement selon que l'on a un texte en clair, un mot de passe ou une date.
-- Olivier Miakinen Troll du plus sage chez les conviviaux : le nouveau venu, avec son clan, s'infiltre dans les groupes de nouvelles. (3 c.)
Ma question principale est : y a-t-il un moyen pour éviter de redéfinir
GetWindowRect vingt fois, avec vingt fois le même code ?
Une solution est de faire redéfinir 20 fois la fonction par un template:
template<class T> class CTBaseUI<T> : public T
{
public:
void GetWindowRect(CRect* rect)
{
T::GetWindowRect(rect);
}
}
class CTextUI : public CTBaseUI<CEdit> { ... }
class CPasswordUI : public CTBaseUI<CEdit> { ... }
class CEnumUI : public CTBaseUI<CComboBox> { ... }
class CDateUI : public CTBaseUI<CDateTimeCtrl> { ... }
Avec cette solution, est-il toujours possible d'avoir des méthodes
spécifiques à chaque classe ? Parce que bien évidemment je ne fais pas
le même traitement selon que l'on a un texte en clair, un mot de passe
ou une date.
--
Olivier Miakinen
Troll du plus sage chez les conviviaux : le nouveau venu, avec
son clan, s'infiltre dans les groupes de nouvelles. (3 c.)
Ma question principale est : y a-t-il un moyen pour éviter de redéfinir GetWindowRect vingt fois, avec vingt fois le même code ?
Une solution est de faire redéfinir 20 fois la fonction par un template:
template<class T> class CTBaseUI<T> : public T { public: void GetWindowRect(CRect* rect) { T::GetWindowRect(rect); } }
class CTextUI : public CTBaseUI<CEdit> { ... } class CPasswordUI : public CTBaseUI<CEdit> { ... } class CEnumUI : public CTBaseUI<CComboBox> { ... } class CDateUI : public CTBaseUI<CDateTimeCtrl> { ... }
Avec cette solution, est-il toujours possible d'avoir des méthodes spécifiques à chaque classe ? Parce que bien évidemment je ne fais pas le même traitement selon que l'on a un texte en clair, un mot de passe ou une date.
-- Olivier Miakinen Troll du plus sage chez les conviviaux : le nouveau venu, avec son clan, s'infiltre dans les groupes de nouvelles. (3 c.)
Sylvain
Olivier Miakinen wrote on 29/05/2006 18:49:
Par ailleurs, sans rien changer d'autre, puis-je au moins remplacer : ((CWnd *)this)->GetWindowRect(rect); par : CWnd::GetWindowRect(rect); dans chaque classe dérivée ? non, GetWindowRect n'est pas une méthode de classe mais d'instance [..]
Je ne comprends pas ce qui gêne, là. Le CTextUI::GetWindowRect() et les autres sont aussi des méthodes d'instance, et il me semblait que l'instance était passée automatiquement dans ce cas là.
c'était une autre annerie, lisant comme un appel à une méthode statique ce qui était - fort justement - un appel "contextualisé". (on devrait s'abstenir en cas de grosse fièvre!)
Sylvain.
Olivier Miakinen wrote on 29/05/2006 18:49:
Par ailleurs, sans rien changer d'autre, puis-je au moins remplacer :
((CWnd *)this)->GetWindowRect(rect);
par :
CWnd::GetWindowRect(rect);
dans chaque classe dérivée ?
non, GetWindowRect n'est pas une méthode de classe mais d'instance [..]
Je ne comprends pas ce qui gêne, là. Le CTextUI::GetWindowRect() et
les autres sont aussi des méthodes d'instance, et il me semblait que
l'instance était passée automatiquement dans ce cas là.
c'était une autre annerie, lisant comme un appel à une méthode statique
ce qui était - fort justement - un appel "contextualisé".
(on devrait s'abstenir en cas de grosse fièvre!)
Par ailleurs, sans rien changer d'autre, puis-je au moins remplacer : ((CWnd *)this)->GetWindowRect(rect); par : CWnd::GetWindowRect(rect); dans chaque classe dérivée ? non, GetWindowRect n'est pas une méthode de classe mais d'instance [..]
Je ne comprends pas ce qui gêne, là. Le CTextUI::GetWindowRect() et les autres sont aussi des méthodes d'instance, et il me semblait que l'instance était passée automatiquement dans ce cas là.
c'était une autre annerie, lisant comme un appel à une méthode statique ce qui était - fort justement - un appel "contextualisé". (on devrait s'abstenir en cas de grosse fièvre!)
Sylvain.
Olivier Miakinen
Le 29/05/2006 21:50, Sylvain m'a répondu :
[...]
Je ne comprends pas ce qui gêne, là. [...]
c'était une autre annerie, lisant comme un appel à une méthode statique ce qui était - fort justement - un appel "contextualisé".
Merci de la confirmation.
(on devrait s'abstenir en cas de grosse fièvre!)
(en même temps, comme on n'a rien d'autre à faire, je comprends que la tentation soit forte d'aller traîner dans les news) ;-)
-- Olivier Miakinen Troll du plus sage chez les conviviaux : le nouveau venu, avec son clan, s'infiltre dans les groupes de nouvelles. (3 c.)
Le 29/05/2006 21:50, Sylvain m'a répondu :
[...]
Je ne comprends pas ce qui gêne, là. [...]
c'était une autre annerie, lisant comme un appel à une méthode statique
ce qui était - fort justement - un appel "contextualisé".
Merci de la confirmation.
(on devrait s'abstenir en cas de grosse fièvre!)
(en même temps, comme on n'a rien d'autre à faire, je comprends que la
tentation soit forte d'aller traîner dans les news) ;-)
--
Olivier Miakinen
Troll du plus sage chez les conviviaux : le nouveau venu, avec
son clan, s'infiltre dans les groupes de nouvelles. (3 c.)
c'était une autre annerie, lisant comme un appel à une méthode statique ce qui était - fort justement - un appel "contextualisé".
Merci de la confirmation.
(on devrait s'abstenir en cas de grosse fièvre!)
(en même temps, comme on n'a rien d'autre à faire, je comprends que la tentation soit forte d'aller traîner dans les news) ;-)
-- Olivier Miakinen Troll du plus sage chez les conviviaux : le nouveau venu, avec son clan, s'infiltre dans les groupes de nouvelles. (3 c.)
kanze
Sylvain wrote:
kanze wrote on 30/05/2006 09:38:
[...]
normalement, les classes dont le nom commence par un C majuscule sont celles fournies par Microsoft
ah bon, y'a un copyright exclusif ???
Non, c'est juste la convention que Microsoft a établi. Si tu veux travailler avec des classes en provenance de Microsoft, le respecter rend ton code plus lisible, et réduire la risque des conflits de nom.
Aujourd'hui, a priori, la solution préférée, c'est les namespace. Mais il y a beaucoup de bibliothèque qui ont vu la jour avant les namespaces. Les bonnes, et même la plupart des moins bonnes, se servent des préfixes de nommage pour éviter les collisions : chez RogueWave, par exemple, les noms de classe commencent tous par RW. Microsoft a choisi, pour des raisons qui me dépasse, C.
un certain nombre de langages plus récents n'ont même pas daigner supporter l'héritage multiple, si essentiel dans ce genre de cas
hmm, critique à peine masqué à AWT et Swing ?? ;)
Pas à Swing, en tout cas. Il a fait ce qu'il a pu avec ce que le langage permet. Mais les problèmes qu'il a par rapport à AWT sont un bon exemple du problème : javax.swing.JComponent est à la fois un composant et une container, parce qu'on n'avait pas la possibilité de créer un javax.swing.JComponent qui dérive de java.awt.Component et un javax.swing.JContainer qui dérive et de javax.swing.JComponent et de java.awt.Container.
Ou est-ce que tu le considères bien que je peux appeler add(Component) sur un JButton, bien que le composant ne serait pas correctement affiché ?
dans ma petite expérience des GUI (en mode texte en Pascal object, mode graphique en Java et en C++ (sur système graphique Win32, X11 et TCL)) l'héritage multiple n'a jamais été indispensable, et je ne l'ai jamais utilisé (par contre les interfaces sont omniprésentes).
Il existe toujours des work-arounds. Mais l'héritage multiple est bien utile chaque fois que tu as une hièrarchie fournie par ailleurs, avec les possibilités de customisation à chaque niveau. Customisation au moyen du modèle template, s'entend ; mais c'est la façon la plus répandue de supporter la customisation dans les hièrarchies des interfaces graphiques. (Quand on a beaucoup de points indépendants de customisation, le modèle stratégie devient vite lourd.)
-- 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
Sylvain wrote:
kanze wrote on 30/05/2006 09:38:
[...]
normalement, les classes dont le nom commence par
un C majuscule sont celles fournies par Microsoft
ah bon, y'a un copyright exclusif ???
Non, c'est juste la convention que Microsoft a établi. Si tu
veux travailler avec des classes en provenance de Microsoft, le
respecter rend ton code plus lisible, et réduire la risque des
conflits de nom.
Aujourd'hui, a priori, la solution préférée, c'est les
namespace. Mais il y a beaucoup de bibliothèque qui ont vu la
jour avant les namespaces. Les bonnes, et même la plupart des
moins bonnes, se servent des préfixes de nommage pour éviter les
collisions : chez RogueWave, par exemple, les noms de classe
commencent tous par RW. Microsoft a choisi, pour des raisons
qui me dépasse, C.
un certain nombre de langages plus récents n'ont même
pas daigner supporter l'héritage multiple, si essentiel
dans ce genre de cas
hmm, critique à peine masqué à AWT et Swing ?? ;)
Pas à Swing, en tout cas. Il a fait ce qu'il a pu avec ce que le
langage permet. Mais les problèmes qu'il a par rapport à AWT
sont un bon exemple du problème : javax.swing.JComponent est à
la fois un composant et une container, parce qu'on n'avait pas
la possibilité de créer un javax.swing.JComponent qui dérive de
java.awt.Component et un javax.swing.JContainer qui dérive et de
javax.swing.JComponent et de java.awt.Container.
Ou est-ce que tu le considères bien que je peux appeler
add(Component) sur un JButton, bien que le composant ne serait
pas correctement affiché ?
dans ma petite expérience des GUI (en mode texte en Pascal
object, mode graphique en Java et en C++ (sur système
graphique Win32, X11 et TCL)) l'héritage multiple n'a jamais
été indispensable, et je ne l'ai jamais utilisé (par contre
les interfaces sont omniprésentes).
Il existe toujours des work-arounds. Mais l'héritage multiple
est bien utile chaque fois que tu as une hièrarchie fournie par
ailleurs, avec les possibilités de customisation à chaque
niveau. Customisation au moyen du modèle template, s'entend ;
mais c'est la façon la plus répandue de supporter la
customisation dans les hièrarchies des interfaces graphiques.
(Quand on a beaucoup de points indépendants de customisation, le
modèle stratégie devient vite lourd.)
--
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
normalement, les classes dont le nom commence par un C majuscule sont celles fournies par Microsoft
ah bon, y'a un copyright exclusif ???
Non, c'est juste la convention que Microsoft a établi. Si tu veux travailler avec des classes en provenance de Microsoft, le respecter rend ton code plus lisible, et réduire la risque des conflits de nom.
Aujourd'hui, a priori, la solution préférée, c'est les namespace. Mais il y a beaucoup de bibliothèque qui ont vu la jour avant les namespaces. Les bonnes, et même la plupart des moins bonnes, se servent des préfixes de nommage pour éviter les collisions : chez RogueWave, par exemple, les noms de classe commencent tous par RW. Microsoft a choisi, pour des raisons qui me dépasse, C.
un certain nombre de langages plus récents n'ont même pas daigner supporter l'héritage multiple, si essentiel dans ce genre de cas
hmm, critique à peine masqué à AWT et Swing ?? ;)
Pas à Swing, en tout cas. Il a fait ce qu'il a pu avec ce que le langage permet. Mais les problèmes qu'il a par rapport à AWT sont un bon exemple du problème : javax.swing.JComponent est à la fois un composant et une container, parce qu'on n'avait pas la possibilité de créer un javax.swing.JComponent qui dérive de java.awt.Component et un javax.swing.JContainer qui dérive et de javax.swing.JComponent et de java.awt.Container.
Ou est-ce que tu le considères bien que je peux appeler add(Component) sur un JButton, bien que le composant ne serait pas correctement affiché ?
dans ma petite expérience des GUI (en mode texte en Pascal object, mode graphique en Java et en C++ (sur système graphique Win32, X11 et TCL)) l'héritage multiple n'a jamais été indispensable, et je ne l'ai jamais utilisé (par contre les interfaces sont omniprésentes).
Il existe toujours des work-arounds. Mais l'héritage multiple est bien utile chaque fois que tu as une hièrarchie fournie par ailleurs, avec les possibilités de customisation à chaque niveau. Customisation au moyen du modèle template, s'entend ; mais c'est la façon la plus répandue de supporter la customisation dans les hièrarchies des interfaces graphiques. (Quand on a beaucoup de points indépendants de customisation, le modèle stratégie devient vite lourd.)
-- 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