unwind-protect

L'utilisation de sauts non locaux peut donc (et c'est la situation normale) influencer la pile d'exécution. L'exécution de certaines fonctions peut être abandonnée de manière prématurée. Voici un exemple de fonction qui modifie un objet global, exécute une autre fonction, puis remet l'objet dans son état d'origine.

* (defparameter *l* (list 1 2 3))

*L*
* (defun h (fun)
    (let ((temp (car *l*)))
      (setf (car *l*) 4)
      (funcall fun)
      (setf (car *l*) temp)
      nil))

H
* (h #'(lambda () (print (car *l*))))

4
NIL
* *l*

(1 2 3)
*

Ici, c'est le premier élément d'une liste qui est modifié, mais souvent il s'agit d'une opération plus compliquée comme l'ouverture ou la modification d'un fichier ou d'une base de données. Cependant, la fonction h ci-dessus ne fonctionne pas en présence d'un saut non local. Voici un exemple avec return-from :

* *l*

(1 2 3)
* (block bonjour
    (h #'(lambda ()
           (print (car *l*))
           (return-from bonjour "finished"))))

4
"finished"
* *l*

(4 2 3)
*

La fonction h doit être protégée de la possibilité que son exécution soit abandonnée de manière prématurée par l'utilisation d'un saut non local. Comme le montre notre exemple ci-dessus, le programmeur de la fonction h ne sait peut-être pas qu'un tel saut est possible.

La solution du problème est l'opérateur spécial unwind-protect. Il prend une expression dont l'évaluation est protégée, puis une suite d'expressions qui sont évaluées même si un saut non local est effectué pendant l'évaluation de l'expression protégée. Voici comment résoudre le problème ci-dessus avec unwind-protect :

* (defparameter *l* (list 1 2 3))

*L*
* *l*

(1 2 3)
* (defun h (fun)
    (let ((temp (car *l*)))
      (setf (car *l*) 4)
      (unwind-protect
          (funcall fun)
        (setf (car *l*) temp))
      nil))

H
* (block bonjour
    (h #'(lambda ()
           (print (car *l*))
           (return-from bonjour "finished"))))

4
"finished"
* *l*

(1 2 3)
*

Irene DURAND
2011-10-14