From: Jeffrey Cunningham
Subject: Help with macro to list structure properties?
Date: 
Message-ID: <pan.2005.05.19.15.04.28.46595@cunningham.net>
Suppose I create a structure and a function which lists its current values
on a line like this:

(defstruct astruct
  (prop1      "abc")
  (prop2      "def")
  (prop3      "xyz"))

(defun list-struct (S)
  (format t "prop1=~A prop2=~A prop3=~A" 
            (astruct-prop1 S) 
            (astruct-prop2 S) 
            (astruct-prop3 S)))

For a small number of slots in the structure, its easy enough, but for a
larger number - and for different structures - it seems to be crying out
for a macro, only I don't know how to go about writing one to do this. My
knowledge of macros is rudimentary at this point. Is there an easy way to
do this for arbitrary structures?

Regards

--jeff cunningham

From: Pascal Bourguignon
Subject: Re: Help with macro to list structure properties?
Date: 
Message-ID: <87acmrjicu.fsf@thalassa.informatimago.com>
Jeffrey Cunningham <·······@cunningham.net> writes:

> Suppose I create a structure and a function which lists its current values
> on a line like this:
>
> (defstruct astruct
>   (prop1      "abc")
>   (prop2      "def")
>   (prop3      "xyz"))
>
> (defun list-struct (S)
>   (format t "prop1=~A prop2=~A prop3=~A" 
>             (astruct-prop1 S) 
>             (astruct-prop2 S) 
>             (astruct-prop3 S)))

Structure containing only printable values are printable.
Structure containing only printable readably values are printable readably.

[4]> (defstruct astruct
  (prop1      "abc")
  (prop2      "def")
  (prop3      "xyz"))

ASTRUCT
[5]> (let ((*print-readably* t))
    (print (make-astruct)))

#S(|COMMON-LISP-USER|::|ASTRUCT| :|PROP1| "abc" :|PROP2| "def" :|PROP3| "xyz") 
#S(ASTRUCT :PROP1 "abc" :PROP2 "def" :PROP3 "xyz")
[6]> (read-from-string (with-output-to-string (s) (let ((*print-readably* t))
                                                    (print (make-astruct) s))))
#S(ASTRUCT :PROP1 "abc" :PROP2 "def" :PROP3 "xyz") ;
79
[7]> 

> For a small number of slots in the structure, its easy enough, but for a
> larger number - and for different structures - it seems to be crying out
> for a macro, only I don't know how to go about writing one to do this. My
> knowledge of macros is rudimentary at this point. Is there an easy way to
> do this for arbitrary structures?

Instead of defininig a new function name, you could override the
PRINT-OBJECT method:

[11]> (defmethod print-object ((self astruct) stream) \
         (format stream "~&#<ASTRUCT ~{~A = ~S~^; ~}>" 
             (list :prop1 (astruct-prop1 self) 
                   :prop2 (astruct-prop2 self) 
                   :prop2 (astruct-prop2 self))) self)
[12]> (make-astruct)
#<ASTRUCT PROP1 = "abc"; PROP2 = "def"; PROP2 = "def">
[13]> 

Have a look at these posts to see how you could automatically generate
such a method at the same time you DEFSTRUCT it.  Otherwise, you may
want to use one of the :print-function or :print-object options to
DEFSTRUCT.


http://groups-beta.google.com/group/comp.lang.lisp/browse_frm/thread/b493742eeb501fa/d53068b7d9c5c133?q=defmacro+group:comp.lang.lisp+author:Pascal+author:Bourguignon&rnum=3&hl=en#d53068b7d9c5c133

http://groups-beta.google.com/group/comp.lang.lisp/browse_frm/thread/df9dae06911aeda8/1bfbcbcb008aed03?q=defmacro+group:comp.lang.lisp+author:Pascal+author:Bourguignon&rnum=28&hl=en#1bfbcbcb008aed03




-- 
__Pascal_Bourguignon__               _  Software patents are endangering
()  ASCII ribbon against html email (o_ the computer industry all around
/\  1962:DO20I=1.100                //\ the world http://lpf.ai.mit.edu/
    2001:my($f)=`fortune`;          V_/   http://petition.eurolinux.org/
From: Jeffrey Cunningham
Subject: Re: Help with macro to list structure properties?
Date: 
Message-ID: <pan.2005.05.20.01.06.54.261525@cunningham.net>
On Thu, 19 May 2005 18:22:41 +0200, Pascal Bourguignon wrote:


> Instead of defininig a new function name, you could override the
> PRINT-OBJECT method:
> 
> [11]> (defmethod print-object ((self astruct) stream) \
>          (format stream "~&#<ASTRUCT ~{~A = ~S~^; ~}>"
>              (list :prop1 (astruct-prop1 self)
>                    :prop2 (astruct-prop2 self)
>                    :prop2 (astruct-prop2 self))) self)
> [12]> (make-astruct)
> #<ASTRUCT PROP1 = "abc"; PROP2 = "def"; PROP2 = "def"> [13]>
> 
> Have a look at these posts to see how you could automatically generate
> such a method at the same time you DEFSTRUCT it.  Otherwise, you may
> want to use one of the :print-function or :print-object options to
> DEFSTRUCT.
> 
> 
> http://groups-beta.google.com/group/comp.lang.lisp/browse_frm/thread/b493742eeb501fa/d53068b7d9c5c133?q=defmacro+group:comp.lang.lisp+author:Pascal+author:Bourguignon&rnum=3&hl=en#d53068b7d9c5c133
> 
> http://groups-beta.google.com/group/comp.lang.lisp/browse_frm/thread/df9dae06911aeda8/1bfbcbcb008aed03?q=defmacro+group:comp.lang.lisp+author:Pascal+author:Bourguignon&rnum=28&hl=en#1bfbcbcb008aed03

From those posts it seemed that there is easy way to automate the listing
of structure elements - although my knowledge of Lisp isn't nearly
sufficient to follow them at any depth. Defining print-object as above is
clearly a better way of doing it than my first attempts, but one still
must write out each slot name as part of the function definition. I was
hoping to get around that.

Thanks,

-jeff cunningham
From: Pascal Bourguignon
Subject: Re: Help with macro to list structure properties?
Date: 
Message-ID: <87d5rmiri7.fsf@thalassa.informatimago.com>
Jeffrey Cunningham <·······@cunningham.net> writes:

> On Thu, 19 May 2005 18:22:41 +0200, Pascal Bourguignon wrote:
>
>
>> Instead of defininig a new function name, you could override the
>> PRINT-OBJECT method:
>> 
>> [11]> (defmethod print-object ((self astruct) stream) \
>>          (format stream "~&#<ASTRUCT ~{~A = ~S~^; ~}>"
>>              (list :prop1 (astruct-prop1 self)
>>                    :prop2 (astruct-prop2 self)
>>                    :prop2 (astruct-prop2 self))) self)
>> [12]> (make-astruct)
>> #<ASTRUCT PROP1 = "abc"; PROP2 = "def"; PROP2 = "def"> [13]>
>> 
>> Have a look at these posts to see how you could automatically generate
>> such a method at the same time you DEFSTRUCT it.  Otherwise, you may
>> want to use one of the :print-function or :print-object options to
>> DEFSTRUCT.
>> 
>> 
>> http://groups-beta.google.com/group/comp.lang.lisp/browse_frm/thread/b493742eeb501fa/d53068b7d9c5c133?q=defmacro+group:comp.lang.lisp+author:Pascal+author:Bourguignon&rnum=3&hl=en#d53068b7d9c5c133
>> 
>> http://groups-beta.google.com/group/comp.lang.lisp/browse_frm/thread/df9dae06911aeda8/1bfbcbcb008aed03?q=defmacro+group:comp.lang.lisp+author:Pascal+author:Bourguignon&rnum=28&hl=en#1bfbcbcb008aed03
>
> From those posts it seemed that there is easy way to automate the listing
> of structure elements - although my knowledge of Lisp isn't nearly
> sufficient to follow them at any depth. Defining print-object as above is
> clearly a better way of doing it than my first attempts, but one still
> must write out each slot name as part of the function definition. I was
> hoping to get around that.

Yes, add two and two.  Or, using this modern and excellent programming
tool: cut-and-paste, I'll write a third post with the exact same
content.  The essence of pedagogy is repeatition...



(DEFMACRO DEFINE-WITH-STRUCTURE-AND-PRINT-OBJECT (NAME-AND-OPTIONS &REST SLOTS)
  "
NAME-AND-OPTIONS:  Either a structure name or a list (name . options).
          Valid options are: (:conc-name prefix).
DO:       Define a macro: (WITH-{NAME} object &body body)
          expanding to a symbol-macrolet embedding body where
          symbol macros are defined to access the slots.
"
  (LET* ((NAME      (IF (SYMBOLP NAME-AND-OPTIONS)
                      NAME-AND-OPTIONS (CAR NAME-AND-OPTIONS)))
         (CONC-NAME (IF (SYMBOLP NAME-AND-OPTIONS)
                      (CONCATENATE 'STRING (STRING NAME) "-")
                      (LET ((CONC-OPT (CAR (MEMBER :CONC-NAME
                                                   (CDR NAME-AND-OPTIONS)
                                                   :KEY (FUNCTION CAR)))))
                        (IF CONC-OPT
                          (SECOND CONC-OPT)
                          (CONCATENATE 'STRING (STRING NAME) "-")))))
         (SLOT-NAMES (MAPCAR (LAMBDA (SLOT) (IF (LISTP SLOT) (CAR SLOT) SLOT))
                             SLOTS)))
    `(PROGN
       (DEFSTRUCT ,NAME-AND-OPTIONS ,@SLOTS)
       (DEFMACRO
           ,(INTERN (WITH-STANDARD-IO-SYNTAX (FORMAT NIL "WITH-~A" NAME)))
           (OBJECT &BODY BODY)
         (IF (SYMBOLP OBJECT)
           `(SYMBOL-MACROLET
             ,(MAPCAR
               (LAMBDA (SLOT)
                 (LIST SLOT
                       (LIST
                        (INTERN (CONCATENATE 'STRING
                                  (STRING ',CONC-NAME) (STRING SLOT)))
                        OBJECT))) ',SLOT-NAMES)
             ,@BODY)
           (LET ((OBJV (GENSYM)))
             `(LET ((,OBJV ,OBJECT))
                (SYMBOL-MACROLET
                 ,(MAPCAR
                   (LAMBDA (SLOT)
                     (LIST SLOT
                           (LIST
                            (INTERN  (CONCATENATE 'STRING
                                       (STRING ',CONC-NAME) (STRING SLOT)))
                            OBJV))) ',SLOT-NAMES)
                 ,@BODY)))))
;;; And here, let's add the print-object method for this structure,
;;; again let's cut-and-paste from above, and substitute the fields:
       (DEFMETHOD PRINT-OBJECT ((SELF ,NAME) STREAM) 
         (FORMAT STREAM "~&#<~A ~{~A = ~S~^; ~}>"
                 ',NAME
                 (LIST
                  ,@(MAPCAN
                     (LAMBDA (SLOT)
                       (LIST
                        (INTERN (STRING SLOT) :KEYWORD)
;;; with some more cut-and-pasting from above with- macro.
                        (LIST (INTERN (CONCATENATE 'STRING
                                        (STRING CONC-NAME) (STRING SLOT)))
                              'SELF)))
                     SLOT-NAMES)))
         SELF))))




[37]> (MACROEXPAND '(DEFINE-WITH-STRUCTURE-AND-PRINT-OBJECT ASTRUCT FIELD1 (FIELD2 "ahah") (FIELD3 5550960)))
(PROGN (DEFSTRUCT ASTRUCT FIELD1 (FIELD2 "ahah") (FIELD3 5550960))
       (DEFMACRO WITH-ASTRUCT (OBJECT &BODY BODY)
         (IF (SYMBOLP OBJECT)
           (CONS 'SYMBOL-MACROLET
                 (CONS
                  (MAPCAR
                   (LAMBDA (SLOT)
                     (LIST SLOT
                           (LIST (INTERN (CONCATENATE 'STRING (STRING '"ASTRUCT-") (STRING SLOT)))
                                 OBJECT)))
                   '(FIELD1 FIELD2 FIELD3))
                  BODY))
           (LET ((OBJV (GENSYM)))
             (LIST 'LET (LIST (LIST OBJV OBJECT))
                   (CONS 'SYMBOL-MACROLET
                         (CONS
                          (MAPCAR
                           (LAMBDA (SLOT)
                             (LIST SLOT
                                   (LIST
                                    (INTERN (CONCATENATE 'STRING (STRING '"ASTRUCT-") (STRING SLOT)))
                                    OBJV)))
                           '(FIELD1 FIELD2 FIELD3))
                          BODY))))))
       (DEFMETHOD PRINT-OBJECT ((SELF ASTRUCT) STREAM)
         (FORMAT STREAM "~&#<~A ~{~A = ~S~^; ~}>" 'ASTRUCT
                 (LIST :FIELD1 (ASTRUCT-FIELD1 SELF) :FIELD2 (ASTRUCT-FIELD2 SELF) :FIELD3
                       (ASTRUCT-FIELD3 SELF)))
         SELF))                         ;
T

Happy?


-- 
__Pascal Bourguignon__                     http://www.informatimago.com/
Our enemies are innovative and resourceful, and so are we. They never
stop thinking about new ways to harm our country and our people, and
neither do we. -- Georges W. Bush
From: Kalle Olavi Niemitalo
Subject: Re: Help with macro to list structure properties?
Date: 
Message-ID: <87mzqp3c9e.fsf@Astalo.kon.iki.fi>
Pascal Bourguignon <···@informatimago.com> writes:

> [11]> (defmethod print-object ((self astruct) stream) \
>          (format stream "~&#<ASTRUCT ~{~A = ~S~^; ~}>" 

Don't forget to signal PRINT-NOT-READABLE when required.
PRINT-UNREADABLE-OBJECT can do this for you.
It'd also display the actual names of subclasses,
should there ever be any.
From: Kent M Pitman
Subject: Re: Help with macro to list structure properties?
Date: 
Message-ID: <uzmuptyp2.fsf@nhplace.com>
Kalle Olavi Niemitalo <···@iki.fi> writes:

> Pascal Bourguignon <···@informatimago.com> writes:
> 
> > [11]> (defmethod print-object ((self astruct) stream) \
> >          (format stream "~&#<ASTRUCT ~{~A = ~S~^; ~}>" 
> 
> Don't forget to signal PRINT-NOT-READABLE when required.
> PRINT-UNREADABLE-OBJECT can do this for you.
> It'd also display the actual names of subclasses,
> should there ever be any.

Right.  Use PRINT-UNREADABLE-OBJECT for other reasons, too.
Like that it knows not to use the ~& before #<.  (You really
should never force a carriage return like that outside your own
expression.  That will seriously mess up a pretty printer.)