Newsgroups: comp.lang.lisp
Path: cantaloupe.srv.cs.cmu.edu!das-news2.harvard.edu!das-news.harvard.edu!news2.near.net!MathWorks.Com!yeshua.marcam.com!charnel.ecst.csuchico.edu!csusac!csus.edu!netcom.com!yost
From: Dave@Yost.COM (Dave Yost)
Subject: with-preserved-values macro
Message-ID: <yostCxJC32.4C5@netcom.com>
Sender: yost@netcom.com (Dave Yost)
Reply-To: Dave@Yost.COM (Dave Yost)
Organization: Dave Yost's house
Date: Wed, 12 Oct 1994 00:59:26 GMT
Lines: 83

Not having been around for 10 years, I was wondering why there wasn't
a macro like this (did I get it right?):

(defmacro with-preserved-values (thing-spec-list &body body)
  "Each thing-spec is a list consisting of a thing
and an optional temporary value to be stored there.
Executes body inside an unwind-protect context which preserves
the original values of the things listed in thing-spec-list.
Example:
  (setf height 1)
  (setf (width foo) 2)
  (with-preserved-values ((height)
			  ((width foo) 9))
    (print height) ; -> 1
    (print (width foo)) ; -> 9
    (setf height 22))
  (print height) ; -> 1
  (print (width foo)) ; -> 2"

  (if (= (length thing-spec-list) 1)
    (let ((oldValue (gensym)))
      (destructuring-bind ((thing &optional (value nil valueSet)))
			  thing-spec-list
	(if valueSet
	  `(let ((,oldValue ,thing))
	     (setf ,thing ,value)
	     (unwind-protect
	       (progn ,@body)
	       (setf ,thing ,oldvalue)))
	  `(let ((,oldValue ,thing))
	     (unwind-protect
	       (progn ,@body)
	       (setf ,thing ,oldvalue))))))

    (let ((numOldValues (length thing-spec-list))
	  (oldValues (gensym)))
      `(let ((,oldValues (make-array ,numOldValues)))
	 (unwind-protect
	   ,(append '(progn)
		    (loop for thing-spec in thing-spec-list
			  for ind from 0
			  collect (destructuring-bind (thing &optional (value nil valueSet))
						      thing-spec
				    (if valueSet
				      `(progn
					 (setf (aref ,oldValues ,ind) ,thing)
					 (setf ,thing ,value))
				      `(setf (aref ,oldValues ,ind) ,thing))))
		    body)
	   ,(append '(progn)
		    (loop for thing-spec in thing-spec-list
			  for ind from 0
			  collect (destructuring-bind (thing &optional value)
						      thing-spec
				    (declare (ignore value))
				    `(setf ,thing (aref ,oldValues ,ind))))))))))

#| Testing

(let ((pv1 3)
      (pv2 4))
  (progn
    (print "Should print 1 2 3 4 5 6 7")
    (ignore-errors
     (with-preserved-values ((pv1) (pv2 2))
       (setf pv1 1)
       (print pv1)
       (print pv2)
       (setf pv2 22)
       (error "hah!")))
    (print pv1)
    (print pv2)
    (setf pv1 7)
    (print (with-preserved-values ((pv1 5))
	     (print pv1)
	     (setf pv1 6)))
    (print pv1)
    (values)))

|#
-- 
  Dave Yost
      @    .COM
