Pour limiter le nombre de variables Publiques/Privées ... Utilisez la Pile

Bonjour,

Lorsque l'on développe une application, on est souvent confronté à la question des variables.

On en déclare des publiques, valables pour l'ensemble de l'appli et des privés, dans les procédures ou les modules de formulaires.

Certaines sont indispensables à la bonne marche de l'appli, mais d'autres ne sont déclarées que pour des besoins temporaires, voire à usage unique.

Comme par exemple pour :

  • inverser deux éléments d'un tableau,
  • permuter deux variables,
  • faire un calcul provisoire,
  • servir de pointeur en sortie de test,
  • etc.

Même si les variables privées, déclarées dans les procédures, sont effacées en sortie, il n'en demeure pas moins qu'elles occupent une certaine place. Ne serait-ce que le code de déclaration (si Option Explicit). D'où l'intérêt d'en limiter le nombre.

Avec la pile, on peut se passer de certaines variables et ne conserver que celles strictement nécessaires.

Pour cela, on utilise une méthode similaire à celle utilisée en langage machine où on se sert d'une pile (Stack) pour y stocker temporairement des données (les registres en général).

Le code suivant rappellera surement de bons souvenirs à ceux qui ont pratiqué ce langage (Asm, Masm, etc.).

(la pile a été initialisée avant)
PUSH AX             On empile AX (l'accumulateur)
PUSH BX             On empile BX (registre de base)
MOV AX, [0140]      AX=contenu de l'adresse 0140
ADD BX, AX          Addition dans BX
MOV [0140], BX      Place le résultat à l'adresse 0140
POP BX              Récupère BX
POP AX              Récupère AX
Le pointeur de pile (SP) est automatiquement mis à jour

Pour notre pile VBA on utilisera un tableau (à une dimension) de type variant (déclaré publique) et deux fonctions très courtes.

Pour simplifier la programmation, et contrairement au langage machine, où la pile est alimentée par le haut, le tableau sera alimenté par le bas.

Ainsi on se passe du pointeur de pile et on utilise le plus grand indice disponible du tableau (Ubound).

On ne devrait plus dire "Empiler" & "Dépiler" dans ce cas mais plutôt "Mettre en dessous" & "Prendre en dessous". On conservera néanmoins ces termes car le résultat est le même.

Déclaration du tableau dans le module principal :

Public Pile() As Variant

La taille du tableau (en mémoire) sera limitée puisqu'après chaque extraction il est redimensionné. A moins d'empiler de nombreuses données (ce qui n'est pas le but), sa taille sera limitée à quelques éléments et nulle lorsque la pile sera vide.

Tous les types de données, pouvant être affectés à un élément de tableau variant, sont acceptés. Les autres types sont exclus (objets ...).

Types autorisés : 2=Integer, 3=Long, 4=Single, 5=Double, 6=Currency, 7=Date, 8=String, 11=Boolean, 17=Byte, 20=LongLong (64bits)

Le type "Variant" est également accepté mais sachant qu'il change au moment de l'affectation, il ne sera pas reconnu au moment du Pop (voir exemple dans le classeur joint).

Les fonctions seront placées dans un module standard et déclarées publiques si nécessaire.

Push pour empiler une donnée

Function Push(ByRef Variable) ' Byref pointe la variable à son emplacement mémoire
' Empile le contenu de la variable dans le Tableau Pile() en dernière position

' Entrée : Variable = donnée à empiler. Si c'est une variable elle n'est pas modifiée par la fonction.
' Sortie : Néant

On Error Resume Next
ReDim Preserve Pile(UBound(Pile) + 1)           ' Ajoute un élément à la pile
If Err.Number <> 0 Then ReDim Pile(1)           ' Erreur si le tableau n'est pas dimensionné : créé 1 élément
Err.Clear
Pile(UBound(Pile)) = Variable                   ' Place en dernière position
If Err.Number <> 0 Then Pile(UBound(Pile)) = "" ' Si la variable est incompatible, une erreur se produit
On Error GoTo 0                                 ' Dans ce cas la pile est alimentée avec "" (pour ne pas bloquer la fonction)
                                                ' ** Ne devrait pas se produire si l'on respecte les types autorisés
End Function

Pop pour dépiler une donnée vers une variable

Public Function Pop(ByRef Variable) ' Byref pointe la variable à son emplacement mémoire
' Retire la dernière donnée de la Pile (tableau Pile()) et l'affecte à la variable cible

' Entrée : Variable = variable de destination ** la variable est modifiée
' Retour : Si le type de la variable destination correspond au type de la donnée stockée :
'           - La fonction retourne 0 (retour non utilisé sauf si nécessaire)
'           - La variable cible est modifiée
'          Si le type ne correspond pas :
'           - La fonction retourne l'élément de la pile (il est supprimé de la pile)
'           - La variable cible est inchangée

On Error Resume Next ' Une erreur 9 se produit si le tableau est vide (Pop avant d'avoir fait un Push par exemple)
                     ' Dans ce cas retourne 0, la variable est inchangée

' Vérification du type de donnée (doit correspondre : Entier vers Entier, Date vers Date ...)
If VarType(Variable) = VarType(Pile(UBound(Pile))) Then
    ' Type ok
    Variable = Pile(UBound(Pile))
    Pop = 0
Else
    ' Type <> Retourne le contenu avant suppression
    Pop = Pile(UBound(Pile))
End If

' Redimensionne la pile (-1 élément) ou raz si vide
If UBound(Pile) = 1 Then Erase Pile Else ReDim Preserve Pile(UBound(Pile) - 1)

On Error GoTo 0
End Function

Les commentaires, placés dans le code, expliquent le fonctionnement.

L'appel des fonctions est très simple, la valeur de retour sera ignorée. Sauf en cas de problème au niveau développement (Voir exemple dans le classeur joint).

Exemples d'appels :

' Sauvegarder une variable
Push a$      ' Empile le contenu de a$ (la pile contiendra 1 élément)
...
Pop a$       ' Récupère a$ (la pile sera vidée)

' Sauvegarder un texte
Push "Mon texte"
...
Pop a$

' Sauvegarder plusieurs données
Text1 = "Texte" ' Du texte
Entier1 = 22300 ' Un entier
Long1 = 785693 ' Un long
Montant1 = 150352.63 ' Une valeur double
Date1 = Now ' Une date
Octet1 = 5 ' Un octet
Boolean1 = True ' Une valeur booléenne
Finance1 = -1523896458.55 ' Une valeur financière
Simple1 = -52360.99 ' Une valeur simple

' On empile le contenu des variables
Push Text1
Push Entier1
Push Long1
Push Montant1
Push Date1
Push Octet1
Push Boolean1
Push Finance1
Push Simple1 ' (la pile contiendra 9 éléments)

' On dépile dans le sens inverse vers d'autres variables (de même type)
' Dernière entrée = première sortie
Pop Simple2
Pop Finance2
Pop Boolean2
Pop Octet2
Pop Date2
Pop Montant2
Pop Long2
Pop Entier2
Pop Text2 ' (la pile sera vidée)

Dans le classeur joint vous trouverez :

  • En Feuil1, un tableau récapitulatif des différents types de variables (Vartype),
  • Dans le Module1 :
    • La procédure "Exemples" avec d'autres exemples d'appels et pour certains, des erreurs avec les corrections.
    • Les procédures "Modif_Références" & "Modif_Références2" qui utilisent la Pile pour renvoyer les réponses à la fermeture d'un Userform.
    • Et bien sûr les deux fonctions Push & Pop.

En exécutant ces procédures en mode "pas à pas" et en utilisant des espions vous pourrez suivre le déroulement et mieux appréhender le fonctionnement.

Avec ces deux fonctions, on peut limiter le nombre de variables utilisées. Moins de déclarations à faire et donc un gain en matière de rédaction et de taille du fichier.

Cependant ce n'est pas une "panacée", l'usage de variables demeure indispensable.

A vous de juger l'utilité de cette méthode (qui peut être améliorée). En tout cas cela aura été un bon exercice.

Eric.

0exemples-pile.xlsm (35.27 Ko)
Rechercher des sujets similaires à "limiter nombre variables publiques privees utilisez pile"