VTK_DEBUG_LEAKS
est actif (Luc va fournir une nouvelle version de ses libdeluques). Ceci permettra de repérer plus facilement les futurs leaks (si vous ne savez pas ce qu'est un leak, il est toujours temps d'apprendre ici).ObjectBrowser
a été nettoyé et quelques bugs ont été corrigés. En particulier, son destructeur est maintenant appelé et la fenêtre se détruit correctement malgré le fait que les objets affichés n'existent plus (ça parait bête mais ça ne l'est pas du tout!). La fermeture de Metafor est en conséquence beaucoup plus rapide qu'avant.
L'interface “rupture” de Pierre-Paul a été nettoyée parce qu'elle n'était pas claire. Avant, le fait qu'un élément soit cassé était décrit par un booléen nommé activity
qui n'a rien à voir avec la classe Activable
de Metafor. Pour manipuler cette variable à partir d'un critère de rupture par exemple, il y avait les traditionnels setActivity()
et getActivity()
mais aussi setActive()
et setInActive()
qui géraient la transition d'un état à l'autre. Pour corser le tout, malgré le fait que les Elements ne soient pas Activable
, il était possible de les désactiver (au sens Activable
du terme) par un appel d'une interface vers l'autre, camouflé dans Interaction
.
Outre le problème de confusion de nom, le plus gros problème, selon moi, est qu'il était possible de casser un élément par setActivity(false)
sans passer par la routine setInActive()
, et donc sans gérer la transition d'état!! On était donc pas loin d'un état “ça marche, j'y touche plus”.
Donc, j'ai renommé tout d'abord activity
en enabled
pour éviter la confusion de nom avec Avtivable
. Les fonctions setActivity()
et getActivity()
deviennent naturellement setEnabled()
etgetEnabled()
. J'ai ensuite fusionné le contenu de setActive()
et setInActive()
dans ces mêmes fonctions pour éviter les problèmes de transition d'état. En rendant setEnabled()
virtuelle, on peut être certain que la transition d'un état à l'autre (cassé ⇔ pas cassé) se fait à un endroit unique! (c'est évidemment à cet endroit que je remets à jour la visu)
J'ai eu de gros problèmes avec cette modif parce que la confusion des deux systèmes d'activation (Activable et rupture) était un moyen de faire fonctionner beaucoup de tests, notamment les tests de soudure (welding
). Pour arriver à faire tout fonctionner avec l'interface nettoyée, j'ai du faire des hypothèses au niveau de l'activation/désactivation des interactions. la logique est la suivante:
Lors d'une transition de stage:
Le problème est le suivant:
DataSet
(un vtkUnstructuredGrid
de VTK pour simplifier) puisqu'il n'est pas possible de supprimer directement une maille d'un maillage dans VTK.
Pour résoudre ce genre de problème, il existe un “Design Pattern” nommé Subject/Observer. Je l'ai déjà utilisé plusieurs fois dans le code (dans Gen4 et pour les DataCurve
, c'est-à-dire la visu des courbes).
Le “sujet” (subject), ici, est l'élément fini qui peut se rompre. Il doit informer la visu d'une manière ou d'une autre qu'il est cassé. Il contient une liste d'observateurs (observers) qui peuvent s'enregistrer auprès de lui (ici, ce sont les objets “drawables” associés de la visu). Quand l'élément se casse, il prévient les observateurs en appellant explicitement une fonction “notify()” qui transmet l'info aux observateurs. Eux font ce qu'ils veulent à ce moment là (ici, ils se déclarent “invalides” pour le remettre à jour lors de la prochaine mise à jour de la visu.
Si on veut un peu complexifier le système, comme par exemple ajouter des arguments aux appels “notify()”, on ajoute le concept d' “événement” (Event
). La fonction notify()
envoie alors aux observateurs un objet de type Event
qui peut contenir autant d'infos qu'on veut. Le sujet peut être vu ainsi comme un générateur d'événements. Il dit “je suis cassé”, “je suis sur le pont de me détruire”, … et n'importe quel objet peut l' “écouter” et réagir à ces événements.
Pour que tout ceci soit codé de manière générique tout en restant simple, j'ai fusionné les interfaces “subject” et “observer” traditionnelles en une seule classe que j'ai nommée EventAware
(en hommage à JCVD, mon acteur préféré).
En pratique, Element
, ElementSet
, Interaction
, InteractionSet
sont awares (EventAware
) et utilisent uiquement l'interface “sujet” de la classe. ElementDrawable
, ElementSetDrawable
, InteractionDrawable
, InteractionSetDrawable
sont aussi awares mais ils utilisent l'interface “observateur”.
Dans d'autres libs, ce type de pattern est poussé encore plus loin. Par exemple, dans VTK, il est possible de mettre des priorités d'appels parmi les observateurs ou même enregistrer un observateur pour écouter un type d'événement particulier (et pas les autres). Dans Qt, ce pattern se limite à tout ce qui n'est pas gérable avec le mécanisme signaux/slots (c'est-à-dire très peu de choses).
Si je me suis cassé la tête à écrire du code générique, c'est évidemment pour pouvoir le réutiliser. Il serait possible de remplacer le vieux code de DataCurve
par ce système. On pourrait aussi imaginer d'utiliser ce code pour mettre à jour certains objets de calcul sans lier explicitement deux objets parfois très éloignés. Par exemple, certains appels à Metafor au milieu du code de calcul pourrait être remplacé par un évémenement que Metafor viendrait écouter si nécessaire.
Ce travail est également un très grand pas vers la visu de maillages dynamique (cas du remaillage par exemple, si j'ai un jour le temps de faire la thèse d'Antoine).
DataCurveBase
et dérivées) en vue d'utiliser le nouveau système d'events au lieu du système un peu trop particularisé aux courbes.MemManager
a été supprimée. Elle permettait de suivre les allocations de mémoire du code si on le compilait avec DEBUGMEM
. Le système était vieux et n'était plus vraiment utilisable sans bidouiller le source (tous les includes Qt plantent, par exemple).VTK_DEBUG_LEAKS
que j'ai appelé METAFOR_DEBUG_LEAKS
. L'idée est simple: chaque fois qu'un objet est alloué, on met à jour une table globale qui compte le nombre d'objets de chaque type. Quand l'objet est détruit, on décrémente la table. Au final, toutes les valeurs doivent valoir 0 si on n'a pas fait l'andouille. Pour des raisons C++, il n'est possible d'effectué ces comptes que pour les objets RefCounted
lorsqu'il sont utilisés +/- correctement (c'est-à-dire qu'on ne les a pas alloué sur la pile et qu'on a pensé à incrémenter au moins 1x leur compteur de référence par un incRef
). DebugLeaks
dans mtGlobal
. Il suffit de compiler le code avec METAFOR_DEBUG_LEAKS
pour avoir un petit tableau récapitulatif des leaks en sortie de Metafor:
Voilà ce que ça donne sur le test apps.ale.testConv2D
par exemple. Le texte suivant est affiché à la fin du test.
==================================== SUMMARY OF POSSIBLE LEAKS ==================================== NAME REMAINING REFS BcInteraction 4 GPointFieldApplicator 2 Geometry 2 Group 4 MaterialSet 1 Mesh 4 Node 120 Point 562 Properties 3 Side 120 Wire 120 non-VirtualObject 3
Dans ce cas-ci, j'ai l'impression que les maillages ALE ne sont pas (plus) détruits. Il reste donc du boulot…
On pourrait imaginer de sommer ces références non détruites et les mettre dans un TSC pour la batterie. C'est ce que je ferai prochainement.
— Romain BOMAN 2010/12/20 09:47