Pointeurs sur fonction membre, héritage, toussa - C++ - Programmation
Marsh Posté le 23-03-2006 à 18:59:34
heu j ai pas tout pige, mais si tu as:
virtual Mere::fct();
virtual Fille1::fct();
virtual Fille2::fct();
virtual Fille3::fct();
...
alors les pointeurs vers toutes ces fonctions sont de aussi type virtual Mere::fct();
Marsh Posté le 23-03-2006 à 19:19:09
Ouaip mais la ca serait plutot un truc du genre :
Code :
|
J'aurais plutot tendance à dire que je vais etre obligé de déclarer plein de virtual void fx() ; dans ma classe de base, mais bon...
edité : j'me rappellais plus comment c'était fait, j'ai remis la map à la place du switch...
Marsh Posté le 23-03-2006 à 19:51:56
> J'aurais plutot tendance à dire que je vais etre obligé de déclarer plein de virtual void fx() ; dans ma classe de base, mais bon...
- exact
Je ne pige pas ce que tu veut faire, ça me parait pas très organisé.
-? Est tu sûr d avoir besoin de N classes filles contenant M fonctions virtuelles?
Une autre organisation:
N classes filles, chacune mères de M classes petite-fille.
Encore une autre organisation:
N*M classes filles
Tu peut mixer ces solutions avec des templates...
Marsh Posté le 23-03-2006 à 22:11:09
En gros je cherche à me faire un outil pour écrire facilement des séquences, pour gérer un processus industriel.
Je dois donc pouvoir lancer des traitements, attendre, vérifier des comptes-rendus, tout en vérifiant en permanence un ensemble de conditions de fonctionnement.
Ma classe Base s'appelle en fait BaseSequence, et Fille est en réalité SequenceTruc.
Mon moteur de séquence fait partie de BaseSequence, et ressemble à ca :
Code :
|
Je peux ainsi mettre dans PreEtape() et PostEtape() mon code de vérification, qui peut lancer ce qu'il veut comme exception si une des conditions nécessaire au fonctionnement n'est pas bonne.
Dans ma classe SequenceTruc, je me déclare des fonctions d'étape, que j'associe à un numéro d'étape en les insérant dans ma map tableEtapes.
Mon problème étant : est ce que je peux raisonnablement stocker des void(SequenceTruc::*)() dans une map qui attend des void(BaseSequence::*)(), sachant que SequenceTruc hérite et n'hérite que de BaseSequence.
Sinon je vois pas comment faire à part me déclarer un nombre suffisant de fonctions "étapes" virtuelles dans ma classe BaseSequence, mais ca fait bourrin je trouve ...
edit : au passage, ca marche comme ca, mais vu que pour insérer mes pointeurs void(SequenceTruc::*)() dans ma map, j'ai casté comme un gros bourrin, j'me dis qu'il y a un truc foireux dans le bazar... Et j'ai pas envie de laisser un truc foireux dans ce code...
edit² : tiens, j'me demande si yaurait pas un holdup à faire à coup de templates... Faudra que je voie ca, demain...
Marsh Posté le 23-03-2006 à 22:31:12
> void(Base::*traitementEtape)();
- fait un typedef, sinon c est pas pratique.
> est ce que je peux raisonnablement stocker des void(SequenceTruc::*)() dans une map qui attend des void(BaseSequence::*)()
- oui à condition de spécifier que c est une fonction virtuelle, je peut pas te donner la syntaxe, mais tu doit avoir ``virtual`` quelque part.
> Sinon je vois pas comment faire à part me déclarer un nombre suffisant de fonctions "étapes" virtuelles dans ma classe BaseSequence, mais ca fait bourrin je trouve ...
- je n ai pas l impression que tu ait besoin d autre chose que:
Code :
|
Cherche sur le web: functionoids
Une bonne doc:
http://snet.wit.ie/GreenSpirit/c++ [...] #faq-33.10
Marsh Posté le 23-03-2006 à 22:33:26
errata:
Code :
|
Marsh Posté le 23-03-2006 à 22:53:10
L'idée d'indiquer que mon pointeur de fonction pointe sur quelque chose de virtual me plait bien, ca me permettrait de valider le fonctionnement de mon bazar comme je l'ai testé aujourd'hui.
Merci pour le lien, j'ai parcouru, je lirai ca calmement demain.
Enfin au pire des cas, je peux écrire une méthode run() par séquence, ca me tuera pas (mais bon c'est domage, le but aurait été de n'avoir de défini dans la classe SequenceTruc que ce qui a rapport directement avec le process de la dite séquence. Histoire d'éviter d'oublier un truc le jour lointain ou je voudrai rajouter une séquence...)
Marsh Posté le 23-03-2006 à 23:35:38
Le coup du pointeur sur fonction viruelle, ça me parait louche.
A mon avis tu doit pouvoir faire autrement.
> Enfin au pire des cas, je peux écrire une méthode run() par séquence
- Alors ça je ne comprends pas... justement le but de faire des étapes virtuelles est d avoir qu un seul run().
Avoir des Etapes fonctionoides te permettraient d ajouter des conditions, et des sauts.
Par exemple une étape condition:
Code :
|
Ensuite tu peut écrire une séquence qui utilise cette etape:
Code :
|
Marsh Posté le 24-03-2006 à 09:44:02
En fait j'suis en train de lire ceci, et j'suis en train d'essayer de comprendre tout le bazar pour voir si ce que je fais ne serait pas autorisé, en fait
Code :
|
edit : et ca. Ouaip enfin ce que j'ai tenté a l'air foireux. Tout le monde n'a pas l'air d'accord la dessus...
Marsh Posté le 24-03-2006 à 11:52:51
ben, moi j ai rien contre les pointeurs sur fonctions virtuelles, mais franchement, c est vraiment bizarre que tu en arrive à ça: les fonctions virtuelle sont faites justement pour éviter d avoir des pointeurs sur fonctions pas pratiques à utiliser.
Tu devrai vraiment lire la doc que je t ai filé, elle explique bien. Je suis quasiment sûr que tu y trouvera ce que tu cherche.
Sinon, j ai pas très bien compris la structure de ton programme, mais est ce que avoir des étapes polymorphes ne suffirait pas?
Marsh Posté le 24-03-2006 à 11:56:01
struct A { virtual void etape(); };
struct B : A { void etape(); };
struct C : A { void etape(); };
A* b=new B();
A* c=new C();
b->etape(); // appelle B::etape();
c->etape(); // appelle C::etape();
Marsh Posté le 24-03-2006 à 15:20:27
Parcequ'à chaque cycle, je veux pouvoir décider de l'étape qui sera executée au cycle d'après.
Et j'ai fait ca avec des pointeurs de fonction. Et pour pas me prendre la tête avec les pointeurs de fonction, bah je les ai planqués derrière une map<int, pointeurDeFonction> et je travaille avec l'int.
Effectivement, si mon downcast de pointeur de fonction s'avérait être véreux à mort, alors il me "suffirait" de définir dans ma classe BaseSequence une tripotées de fonctions virtuelles :
Code :
|
C'est vrai qu'avec cette solution, j'évite d'avoir à remplir mon tableau de pointeurs dans l'initialisation des séquences.
Voila à quoi ressemble le code, et ce que j'ai trouvé jusqu'à maintenant :
SequenceInitialisation.cpp
Code :
|
SequenceInitialisation.h
Code :
|
BaseSequence.cpp
Code :
|
BaseSequence.h
Code :
|
Ceci compile sans problème (Visual C++ 7.0.9500), et fonctionne aussi. Pour tester cette histoire de cast de pointeur de fonction membre, je me suis déclaré une classe bidon (en lui mettant des méthodes virtuelles), et j'ai modifié l'héritage de SequenceInitialisation en :
Code :
|
J'obtiens alors à la compilation un warning de niveau 1 (C4407) : Cast entre différentes représentations du pointeur membre, le compilateur peut générer du code incorrect.
L'aide pour ce warning me propose alors d'inverser l'ordre de l'héritage :
Code :
|
Et effectivement, si j'écris ca ca fonctionne :
Code :
|
Ce que j'en retire, c'est que le cas a été prévu dans le compilo de visual studio (si quelqu'un pouvait tester avec le 2005...), mais je ne sais toujours pas si c'est quelque chose d'autorisé par le standard.
Après on peut faire un sondage : vous préférez la version pointeurs de fonctions, ou la version masse fonctions virtuelles (si je finis par choisir cette dernière, va falloir que je me ponde une macro ou kekchose pour me générer les X fonctions de base )
Marsh Posté le 24-03-2006 à 16:32:47
> Parcequ'à chaque cycle, je veux pouvoir décider de l'étape qui sera executée au cycle d'après.
- Dans ce cas tu peut faire retourner à Etape::postEtape() une pointeur vers l étape suivante.
> Et pour pas me prendre la tête avec les pointeurs de fonction, bah je les ai planqués derrière une map<int, pointeurDeFonction>
- Au lieu de stocker des pointeurs sur fonction, tu peut directement stocker des étapes.
Relis le code que j ai posté plus haut, il fait la même chose, mais il est plus simple à lire: ne necessite pas plein de fonctions virtuelles, ne necessite pas de pointeur sur fonction, permet une meilleure organisation de ton programme.
Il me semble que tu n a pas saisi comment fonctionnent les classes abstraites et les fonctions virtuelles. Je ne saurais que te consiller _vivement_ de lire une doc sur le sujet. Un petit coup de google et tu trouvera plein de tutoriaux.
Marsh Posté le 24-03-2006 à 17:42:55
Si je décide de remplacer mes étapes "fonctions" par des étapes "class etape", ca va me donner un truc du genre :
Code :
|
J'suis désolé mais la je voie pas ou c'est plus simple...
Ce qui serait cool c'est que les mecs qui passeront derrière moi n'aient pas besoin d'être experts en C++ pour modifier le comportement d'une séquence... (avec ton histoire, j'imagine même pas la tête du pavé que je vais trouver lors de l'instanciation de la séquence, pour laquelle va falloir instancier toutes les étapes et leur renseigner tous les pointeurs vers les étapes suivantes...)
Le "modèle" que j'ai en tête, c'est le truc con :
Code :
|
.....
Boarf. En écrivant ca, j'suis en train de me demander si j'aurais pas mieux fait de faire directement mon switch comme un bourrin sans chercher compliqué. La avec mon idée de départ, j'ai remplacé un switch par une map avec des pointeurs de fonction .
Merde je suis trop con des fois . J'ai du fumer un truc pas clair depuis hier...
Ca sert des fois, de tout écrire sur le forum, on se rend compte qu'en fait on cherche à faire un truc hyper tordu
Marsh Posté le 24-03-2006 à 17:52:50
Pour l'instanciation passe par une Factory .
LE couple factory+functor est LA maniere de régler ces problemes. Au pire tu generes un ficheir txt genre xml qui contient la luiste des instanciations, tu le lit et le passe à la factory qui te renvoie l'objet sequence tout pret.
le switch ... autant faire du C
Marsh Posté le 24-03-2006 à 18:44:11
> (avec ton histoire, j'imagine même pas la tête du pavé que je vais trouver lors de l'instanciation de la séquence, pour laquelle va falloir instancier toutes les étapes et leur renseigner tous les pointeurs vers les étapes suivantes...)
Bof, reprends l exemple que je t ai donné des la classe VerificationPorte: tu passe au constructeur de la classe Etape un pointeur vers l étape suivante. Tu peut même en avoir plusieurs:
Code :
|
Correspond à la séquence:
- FermerPorte
- VerifierPorte, si problème ArretUrgence
- sinon ChaufferFour
- puis OuvrirPorte
Tu peut changer les étapes suivantes au cours de la séquence, tu peut ajouter des conditions ou des variables globales dans les constructeurs...
Tu peut simuler des conditions, des boucles, des sauts...
Et en prime tu peut avoir une belle IHM sur tout ça pour gérer indépendamment les étapes et les séquences, faire des sous-séquences, définir des conditions ou variables globales, contrôler l execution de la séquence pas à pas, enregistrer/charger des séquences prédéfinies...
Sans IHM, à mon avis tu te prends la tête, autant coder directement en C ou même en BASIC. C est simple le BASIC.
Avec le switch, tu va beaucoup plus galérer, comme avec tes pointeurs de fonction. Enfin, je te laisse le découvrir.
Marsh Posté le 24-03-2006 à 19:14:49
++fab a écrit : Joelf> Un feed-back de Loki ? |
loki c'est gros, gras, enorme.
Depuis que j'utilise des factory, des typelistes ou des SmallObjects, mon poil est plus brillant et mon code
a les yeux qui pétillent
Blague à part : l'utiliser c'est l'adopter. En outre, c'est une vrai source d'inspiration. Grace aux typelist et multi-stratégies, pas mal de ems interfaces et de mes codes ont gagné en lisibilité//flexibilité
Marsh Posté le 25-03-2006 à 12:55:20
nargy a écrit : [...] |
Ouaip ca serait top, je me coderais bien un système complet d'écriture d'automatismes à la norme IEC 61131-3, avec l'IDE, les modules d'entrées sorties toussa.
Sauf que le projet est à terminer pour hier.
Je pense rester sur le switch (vais virer les pointeurs de fonction), avec une enum pour les numéros d'étape (histoire que l'IHM qui observe la séquence ne pete pas les plombs si j'insere une étape en plein milieu du bazar).
Avec le switch, je ne pense pas galérer, vu qu'il n'est pas prévu qu'il puisse y avoir deux étapes actives dans la même séquence. Au pire si ya besoin de ce genre de chose, alors j'utiliserai deux séquences.
Citation : [...] Loki [...] |
Ca m'a l'air sympa, ca. Je mets ca dans ma liste des trucs-à-regarder-quand-j'aurais-du-temps...
Marsh Posté le 23-03-2006 à 18:43:54
Note : le problème n'est pas un problème pour déclarer ou appeller une fonction membre à travers un pointeur.
Note² : j'ai lu la premiere page des sujets C++ donnés par la recherche : "pointeur fonction membre". J'ai lu aussi quelques truc sur codeproject (dans ce genre la).
Imaginons une classe de base (
class Base ;
) et une classe dérivée (
class Fille ;
).
Dans une des méthodes de la classe Base, on trouve un :
void (Base::* fonctionAAppeller)() f = kekchoseQuiMeDonneUneFonctionAAppeller() ;
La feinte, c'est que ce
kekchoseQuiMeDonneUneFonctionAAppeller()
est virtuel, et renvoie en réalité un
void (Fille::*)()
.
La question : Puis-je travailler en considérant des pointeurs sur fonction membre d'une classe dérivée comme pointeurs sur fonction membre de la classe parente ? (le but serait d'éviter d'avoir à écrire 500
virtual void Base::fonction_xxx() {}
, avec xxx de 000 à 499 !
Note : Base peut hériter multiplement de plein de trucs, mais Fille n'hérite que de Base.
J'ai bien une petite idée de la réponse, vu que pour que ca marche il a fallu que je caste mon pointeur comme un gros barbare, mais on sait jamais, quelqu'un a peut être déja fait ce genre de trucs...