From: Paul von Hippel
Subject: Question:  macros and defstruct
Date: 
Message-ID: <1993Sep8.052732.29301@leland.Stanford.EDU>
This question asks how to use macros to get around the cumbersome access  
functions built into defstruct.

My program uses the following user defined data-structure.

(defstruct stimulus-class 
  (fraction-late 0)
  (dB-quieter 0)
  (answers nil)
  (threshold-range '(0 0))
  (above-threshold nil)
  (enough-answers nil)
  )

whose components can, as far as I know, only be accessed by such forms as 

(stimulus-class-fraction-late class)

where class is a variable of the defined type.  I have defined macros that  
make this a little more concise, viz.

(defmacro above-threshold ()
  `(stimulus-class-above-threshold *class*)
  )

(defmacro below-threshold ()
  `(stimulus-class-below-threshold *class*)
  )

(defmacro dB-quieter ()
  `(stimulus-class-dB-quieter *class*)
  )

(defmacro fraction-late ()
  `(stimulus-class-fraction-late *class*)
  )

(defmacro enough-answers ()
  `(stimulus-class-enough-answers *class*)
  )

(defmacro threshold-range ()
  `(stimulus-class-threshold-range *class*)
  )

where *class* is a global variable.

Obviously, there is a lot of duplicated code here.  Not only do all the  
macros have a parallel definitions, each one uses its name (e.g.,  
threshold-range) in its definition (e.g., stimulus-class-threshold-range).   
I wonder if there is a way to define all these macros - or similar ones -  
in one fell swoop.

From: Barry Margolin
Subject: Re: Question:  macros and defstruct
Date: 
Message-ID: <26qgf9INNeuj@early-bird.think.com>
In article <·····················@leland.Stanford.EDU> ···@cmn10.Stanford.EDU (Paul von Hippel) writes:
>(defstruct stimulus-class 
>  (fraction-late 0)
>  (dB-quieter 0)
>  (answers nil)
>  (threshold-range '(0 0))
>  (above-threshold nil)
>  (enough-answers nil)
>  )
...
>(defmacro above-threshold ()
>  `(stimulus-class-above-threshold *class*)
>  )
... etc.

>Obviously, there is a lot of duplicated code here.  Not only do all the  
>macros have a parallel definitions, each one uses its name (e.g.,  
>threshold-range) in its definition (e.g., stimulus-class-threshold-range).   
>I wonder if there is a way to define all these macros - or similar ones -  
>in one fell swoop.

You can the same technique as DEFSTRUCT itself uses to construct its access
function definitions:

(defmacro define-global-accessors (structure-name global-variable &body slots)
  (labels ((make-accessor-name (slot)
 	     (intern (format nil "~A-~A" structure-name slot)))
	   (make-access-form (slot)
	     (list (make-accessor-name slot) global-variable))
           (make-accessor-definition (slot)
	     `(defmacro ,slot ()
	        ',(make-access-form slot))))
    `(progn ,@(mapcar #'make-accessor-definition slots))))

You can then write:

(define-global-accessors stimulus-class *class*
  fraction-late dB-quieter answers threshold-range above-threshold
  enough-answers)

You can even get rid of the redundancy of having both the
DEFINE-GLOBAL-ACCESSORS and the DEFSTRUCT, by having
DEFINE-GLOBAL-ACCESSORS include the DEFSTRUCT in its expansion.  However,
this means that its syntax must be made more complex, so that you provide
it all the information that DEFSTRUCT might need, and the new macro will
have to know how to parse this info.  Here it is:

(defmacro define-global-structure (name-and-options global-variable
				    &body slot-specs)
  (let ((name (if (consp name-and-options)
		  (car name-and-options)
		  name-and-options))
	(slot-names (mapcar #'(lambda (spec)
				(if (consp spec)
				    (car spec)
				    spec))
			    slot-specs)))
    `(progn
       (defstruct ,name-and-options ,@slot-specs)
       (defvar ,global-variable (,(intern (format nil "MAKE-~A" name))))
       (define-global-accessors ,name ,global-variable ,@slot-names))))

Then you can write:

(define-global-structure stimulus-class *class*
  (fraction-late 0)
  (dB-quieter 0)
  (answers nil)
  (threshold-range '(0 0))
  (above-threshold nil)
  (enough-answers nil)
  )

By the way, the Lisp Machine version of DEFSTRUCT, and perhaps also the
MacLisp version that it came from, has a built-in feature that's similar to
this.  It has a :DEFAULT-POINTER defstruct option, which specifies a form
to use as the default for the argument to all the accessor functions.
-- 
Barry Margolin
System Manager, Thinking Machines Corp.

······@think.com          {uunet,harvard}!think!barmar
From: Jonathan Kaye
Subject: Re: Question:  macros and defstruct
Date: 
Message-ID: <146251@netnews.upenn.edu>
No one seems to have mentioned the simplest solution to this (yet), namely
using the :conc-name option to defstruct.  From CLtL2, p. 476,

  :conc-name
  This provides for automatic prefixing of names of access
  functions....The argument to the :conc-name option specifies an
  alternate prefix to be used....If NIL is specified as an argument,
  then NO prefix is used; then the names of the access functions are the
  same as the slot-names, and it is up to the user to name the slots
  reasonably.

So you would write

  (defstruct (name-of-struct (:conc-name nil))
	slot1
	slot2
	...)

and then you can refer to the slots by the slot name without the
struct prefix.

-jonathan
From: Len Charest
Subject: Re: Question:  macros and defstruct
Date: 
Message-ID: <1993Sep10.204343.25185@jpl-devvax.jpl.nasa.gov>
This post has been adequately aswered by others, but I'd thought I'd comment on the DEFSTRUCT :CONC-NAME option and its relevance to the problem.

In article ·····@leland.Stanford.EDU, ···@cmn10.Stanford.EDU (Paul von Hippel) writes:
>This question asks how to use macros to get around the cumbersome
access functions built into defstruct. 

Language is subjective. (Actually, language is a virus, but that's another thread.)

>My program uses the following user defined data-structure.
>
>(defstruct stimulus-class 
>  (fraction-late 0)
    [ ... ]
>  )
>whose components can, as far as I know, only be accessed by such forms as 
>(stimulus-class-fraction-late class)

You should investigate the options available to DEFSTRUCT, specifically
:CONC-NAME. This option controls the prefix that is used to construct
accessor function names from slot names. If the value of :CONC-NAME is
NIL then no prefix is used. Your structure could be written as:

(defstruct (stimulus-class 
            (:conc-name nil))
  (fraction-late 0)
    [ ... ]
  )

> I have defined macros that make this a little more concise, viz.
>
>(defmacro above-threshold ()
>  `(stimulus-class-above-threshold *class*)
>  )
>where *class* is a global variable.

The :CONC-NAME option shown above solves the conciseness requirement,
but remember that the accessors will be functions of one argument.
There is no portable way using plain DEFSTRUCT to make the argument to
an accessor default to *class*.
..................................................
                                  Len Charest, Jr.
                 JPL Artificial Intelligence Group
                          ·······@aig.jpl.nasa.gov