From: T. V. Raman
Subject: Traversing all the fields of a structure:
Date: 
Message-ID: <1992Apr20.211748.3877@cs.cornell.edu>
Given that I have a structure defined with a set of fields, how do I
iterate down all the fields of the structure given an instance of the
structure?

I could do away with the structure and use simple lists, but then I
will have to manage the accessors as well.

I could of course specify that the struct be stored as a list, but if
I do so is it safe to apply ordinary lisp iterators to the structure?
I am a bit hesitant to do this because it means taking advantage of
the fact that the structure has been implemented as a list.

Is there a nice way of iterating down all the fields of a structure
without resorting to such hacks?

Thanks,

--Raman
-- 
   T. V. Raman <·····@cs.cornell.edu>Tel: (607)255-7421 R 272-2435
		       Office: 5162 Upson Hall,
Department of Computer Science, Cornell University Ithaca NY 14853-6201
		Res: 226 Bryant Avenue Ithaca NY 14850

From: Barry Margolin
Subject: Re: Traversing all the fields of a structure:
Date: 
Message-ID: <kv8m09INNnib@early-bird.think.com>
In article <·····················@cs.cornell.edu> ·····@cs.cornell.edu (T. V. Raman) writes:
>Given that I have a structure defined with a set of fields, how do I
>iterate down all the fields of the structure given an instance of the
>structure?

(defstruct struct
  slot1 slot2 slot3 ...)

(defconstant *structure-slot-accessors*
  (list #'struct-slot1 #'struct-slot2 #'struct-slot3 ...))

(defun map-struct-slots (function struct)
  (dolist (accessor *structure-slot-accessors*)
    (funcall function (funcall accessor struct))))

There's no portable way to generate the list of accessors, nor access
structure slots without using the accessors.  The FAQ posting contains some
implementation-specific information about accessing slots, though.

>I could of course specify that the struct be stored as a list, but if
>I do so is it safe to apply ordinary lisp iterators to the structure?

Of course; it's still just a list.  DEFSTRUCT is merely being used to
define accessor functions for clarity.

>I am a bit hesitant to do this because it means taking advantage of
>the fact that the structure has been implemented as a list.

But that's the whole reason for using the :TYPE option!  If you didn't want
to take advantage of the representation, you wouldn't specify it.
-- 
Barry Margolin
System Manager, Thinking Machines Corp.

······@think.com          {uunet,harvard}!think!barmar
From: ··@ki2.informatik.uni-hamburg.de
Subject: traversing all the fields of a structure:
Date: 
Message-ID: <1992Apr24.130447.26810@informatik.uni-hamburg.de>
In article <············@early-bird.think.com> ······@think.com (Barry Margolin) writes:
> There's no portable way to generate the list of accessors, nor access
> structure slots without using the accessors.  The FAQ posting contains some
> implementation-specific information about accessing slots, though.

I would like to suggest the following:

The idea is to define a package "struct" which provides a defstruct macro that
expands to the "old" defstruct from Common Lisp plus some additional code
that is responsible for storing structure information into a data structure.
Once defined, a client package might use COMMON-LISP while importing
the symbol struct:defmacro and shadowing the original cl:defmacro.
The function shadowing-import might be useful to import struct:defmacro into
cl-user.

The code might not be too efficient (e.g., structure-slot-value), but I think
the suggested implementation strategy is straightforward and
can be called portable.

Further suggestions welcome...

Ralf

PS: Feel free to send me a note if you would like to have the complete code.

;;; -*- Package: USER; Syntax: Common-Lisp; Base: 10 -*-

(in-package :cl-user)

(defpackage struct
  (:use "COMMON-LISP")
  (:shadow common-lisp:defstruct)
  (:export
    "DEFSTRUCT"
    "STRUCTURE-SLOTS"
    "STRUCTURE-SLOT-VALUE"))

;;; ==============================================================================

;;; -*- Package: STRUCT; Syntax: Common-Lisp; Base: 10 -*-

(in-package :struct)


(defvar *structure-info* (make-hash-table))

(defun find-structure-info (struct-name)
  (gethash struct-name *structure-info*))

(defun (setf find-structure-info) (new-info struct-name)
  (future-common-lisp:setf (gethash struct-name *structure-info*) new-info))

(common-lisp:defstruct structure-info
  name
  conc-name
  slot-names
  slot-descriptions)

(common-lisp:defstruct structure-slot-info
  name
  reader-name
  reader
  writer)

(defmacro defstruct (&whole defstruct-form description &rest slots)
  (labels ((extract-slot-names (slot-descriptions)
	     ...)
	   (find-conc-name (description default-conc-name)
	     ...)
	   (extract-slot-accessors (slot-names conc-name)
	     (mapcar #'(lambda (slot-name)
			 (let ((reader-name
				 (intern (concatenate 'string
						      conc-name
						      (symbol-name slot-name)))))
			 (list 'make-structure-slot-info
			       :name `(quote ,slot-name)
			       :reader-name `(quote ,reader-name)
			       :reader
			       `(function ,reader-name)
			       :writer
			       `(function
				 ,(list 'setf
					reader-name)))))
		     slot-names)))
 (let* ((structure-name (if (consp description)
			    (first description)
			     description))
	(conc-name (find-conc-name description structure-name))
	(slot-names (extract-slot-names slots))
	(slot-accessors (extract-slot-accessors slot-names conc-name)))
    `(progn
       (common-lisp:defstruct ,description .,slots)
       (setf (find-structure-info ',structure-name)
	     (make-structure-info
	       :name ',structure-name
	       :conc-name ',conc-name
	       :slot-names ',slot-names
	       :slot-descriptions (list . ,slot-accessors)))))))



(defun structure-slots (structure-name &optional (error-if-structure-unknown t))
  (let ((structure-info (find-structure-info structure-name)))
    (if (and (null structure-info) error-if-structure-unknown)
	(error "Unknown structure ~S."
	       structure-name))
    (if (null structure-info)
	nil
	(structure-info-slot-names structure-info))))


(defun structure-slot-reader (structure-name slot-name
				 &optional (error-if-structure-unknown t)
				 (error-if-slot-unknown t))
  (labels ((find-slot-reader (slot-descriptions slot-name error-if-slot-unknown)
	     (if (endp slot-descriptions)
		 (if error-if-slot-unknown
		     (error "Unknown slot ~S in structure ~S"
			    slot-name structure-name)
		     nil)
		 (let ((slot-description (first slot-descriptions)))
		   (if (eq slot-name (structure-slot-info-name slot-description))
		       (structure-slot-info-reader slot-description)
		       (find-slot-reader (rest slot-descriptions)
					 slot-name
					 error-if-slot-unknown))))))
    (let ((structure-info (find-structure-info structure-name)))
      (if (and (null structure-info) error-if-structure-unknown)
	  (error "Unknown structure ~S."
		 structure-name))
      (if (null structure-info)
	  nil
	  (find-slot-reader (structure-info-slot-descriptions structure-info)
			    slot-name error-if-slot-unknown)))))

(defun structure-slot-writer (structure-name slot-name
				 &optional (error-if-structure-unknown t)
				 (error-if-slot-unknown t))
  ...)

(defun structure-slot-value (structure-instance slot-name)
  (funcall (structure-slot-reader (type-of structure-instance) slot-name)
	   structure-instance))

(defun (setf structure-slot-value) (new-value structure-instance slot-name)
  (funcall (structure-slot-writer (type-of structure-instance) slot-name)
	   new-value
	   structure-instance))

;;; ================================================================================


(shadowing-import
  '(struct:defstruct struct:structure-slot-value struct:structure-slots)
  <my-package>)

or

(defpackage my-package
  (:use "COMMON-LISP")
  (:shadowing-import struct:defstruct struct:structure-slot-value))
From: Len Charest
Subject: Re: traversing all the fields of a structure:
Date: 
Message-ID: <1992Apr24.212957.321@jpl-devvax.jpl.nasa.gov>
In article <······················@informatik.uni-hamburg.de>, ··@ki2.informatik.uni-hamburg.de writes:
|> In article <············@early-bird.think.com> ······@think.com (Barry Margolin) writes:
|> > There's no portable way to generate the list of accessors, nor access
|> > structure slots without using the accessors. 
|> 
|> The idea is to define... a defstruct macro that
|> expands to the "old" defstruct from Common Lisp plus some additional code
|> that is responsible for storing structure information into a data structure

Every Common Lisp implementation itself must cache this structure slot information (e.g., for use with the #S reader macro). IMO, the quickest and most painless way to solve the original poster's problem is to define a function that gets the slots names by calling the implementation-dependent slot-retrieval function(s). Example (untested):

(defun structure-slot-accessors (struct)
  (mapcar #'symbol-function
          #+lucid (loop with type = (sys:structure-type struct)
                             for slot-index in (sys:structure-info type)
                             collect (second (multiple-value-list
                                              (sys:defstruct-slot-info 
                                               type slot-index))))
          #-lucid (error "Teach me how to find the slot names of ~a."
                         struct)
   ))

(defun map-slots (fn struct)
  (loop for accessor in (structure-slot-accessor struct)
        collect (funcall fn (funcall accessor struct))))
..................................................
                                  Len Charest, Jr.
                 JPL Artificial Intelligence Group
                          ·······@aig.jpl.nasa.gov