From: Nathan Sidwell
Subject: table of setf-methods
Date: 
Message-ID: <32D3D3D5.6F8B@pact.srf.ac.uk>
Hi,
I have a structure containing many named flags, ie
(Defstruct flags
	flag1
	flag2
	...
	flagn)

each flag has an associated command line switch (a string) and
I have a list of mappings from string to 'set function', ie

(defconst flag-names
	'(("-flag1"	flags-flag1)
	  ("-flag2"	flags-flag2)
	  ....))

now what I need to do is scan  the command line args (a list
of strings) looking for matches and then set the associated flag
in a global variable. The code similar to,

(setf action (find-flag flag-string))	;; locate the matching flag
;; action will be flags-flag1 etc
(eval `(setf (,action *flags*) t))

Now this uses eval, which I'm led to believe is bad. I'd
like to know a better way of doing what I want. I'm quite happy
for the action to be a function or lambda list so that I can
do (funcall action *flags*), but I can't figureout a non-verbose way
of doing the equivalent of

(defconst flag-names
	`(("flag1" ,#'(lambda (var) (setf (flags-flag1 var) t)))
	  ...))

which I think is what I aught to be doing. I'd like some kind
of macro which expands to the right thing, but can't see how
to do this without evaluating the result of the macro expansion.

Can anyone help?

nathan

-- 
Nathan Sidwell                    The windy road is more interesting
Chameleon Architecture Group at SGS-Thomson, formerly Inmos
http://www.pact.srf.ac.uk/~nathan/                  Tel 0117 9707182
······@pact.srf.ac.uk or ······@bristol.st.com

From: Thomas A. Russ
Subject: Re: table of setf-methods
Date: 
Message-ID: <ymipvzfoku3.fsf@hobbes.isi.edu>
 > I have a structure containing many named flags, ie
 > (Defstruct flags
 > 	flag1
 > 	flag2
 > 	...
 > 	flagn)
 > 
 > each flag has an associated command line switch (a string) and
 > I have a list of mappings from string to 'set function', ie

Use your second version here, slightly modified.

 > (defconst flag-names
 > 	`(("flag1" . ,#'(lambda (var) (setf (flags-flag1 var) t)))
     ;; Added "."  ^here
 > 	  ...))
 >
 > now what I need to do is scan  the command line args (a list
 > of strings) looking for matches and then set the associated flag
 > in a global variable. The code similar to,
 > 
 > (setf action (find-flag flag-string))	;; locate the matching flag
 > ;; action will be flags-flag1 etc
 > (eval `(setf (,action *flags*) t))

(defun set-flag (flag-structure flag-setters flag-string)
  (let ((setter (cdr (assoc flag-string flag-setters :test #'string=))))
     (if setter
       (funcall setter flag-structure t)
       (warn "`~A' is an unrecognized flag" flag-string))) )

 > which I think is what I aught to be doing. I'd like some kind
 > of macro which expands to the right thing, but can't see how
 > to do this without evaluating the result of the macro expansion.

(defmacro defflags (name &rest flag-names)
  ;; Defines a defstruct "name" and a global constant "*name-SETTERS*"
  ;;   which defines an ALIST of string names to setter functions.
  (let ((alist-name (intern (format nil "*~A-SETTERS*" name)))
	(alist-entries
	 (loop for fname in flag-names
	       ;; For each flag, generate the name of its accessor
	       as accessor-name = (intern (format nil "~A-~A" name fname))
	       ;; Collect the lowercase string of the flag name and
	       ;;  a function that will set the appropriate value.  Note
	       ;;  that we build a CONS program in order to have the #'
	       ;;  evaluated when we define the constant value.
	       collect `(cons ,(string-downcase (symbol-name fname))
              	              #'(lambda (v val)
                                  (setf (,accessor-name v) val))))))
                          ;; This function will just set the value of
                          ;;  the flag to T.  If you need more
                          ;;  flexibility, then make it a function of
                          ;;  two arguments and use the second argument
                          ;;  as the value.
   `(progn
      (defstruct ,name ,@flag-names)
      (defconstant ,alist-name
           ;; Uses LIST to force evaluation of the entries, thus
	   ;;  making sure the #' is respected and allowing compilation
	   ;;  of the functions.
           (list ,@alist-entries)))))






-- 
Thomas A. Russ,  USC/Information Sciences Institute          ···@isi.edu    
From: Barry Margolin
Subject: Re: table of setf-methods
Date: 
Message-ID: <5b1i04$qsm@tools.bbnplanet.com>
In article <·············@pact.srf.ac.uk>,
Nathan Sidwell  <······@pact.srf.ac.uk> wrote:
>(defconst flag-names
>	`(("flag1" ,#'(lambda (var) (setf (flags-flag1 var) t)))
>	  ...))
>
>which I think is what I aught to be doing. I'd like some kind
>of macro which expands to the right thing, but can't see how
>to do this without evaluating the result of the macro expansion.

Yes, this is what you need to do.

Anyway, here's how you can generate the table more automatically:

(defconst flag-names
  (mapcar #'(lambda (field)
	      (list (string-downcase field)
		    (coerce `(lambda (var)
			       (setf (,(intern (concatenate 'string
						  "FLAGS-" (symbol-name field)))
				      var) t))
			    'function)))
    '(flag1 flag2 flag3 ...)))

As you can see, this isn't really clearer than your above code, although it
could be made more understandable by splitting it up into separate
functions whose names indicate what they're doing.  It would also take
quite a smart compiler to perform the (coerce ... 'function) at compile
time.  In practice, I would probably use your code, and use an editor macro
to generate the table for me.

Another way to do it would be to use a macro to define "normal" functions
that do the setf

(defmacro def-setf-to-t (structure field)
  `(defun ,(intern (concatenate 'string "SET-" (symbol-name structure)
		                        "-" (symbol-name field)))
          (var)
     (setf (,(intern (concatenate 'string (symbol-name structure)
				  "-" (symbol-name field)))
            var)
       t)))

(def-setf-to-val flags flag1 t)
(def-setf-to-val flags flag2 t)
...

(defconst flag-names
  '(("flag1" #'set-flags-flag1)
    ("flag2" #'set-flags-flag2)
    ...))
-- 
Barry Margolin
BBN Planet, Cambridge, MA
······@bbnplanet.com -  Phone (617) 873-3126 - Fax (617) 873-5508
(BBN customers, please call (800) 632-7638 option 1 for support)
From: Nathan Sidwell
Subject: Re: table of setf-methods
Date: 
Message-ID: <32D4ED03.69CE@pact.srf.ac.uk>
I wrote:
> 
> Hi,
> I have a structure containing many named flags, ie
> (Defstruct flags
>         flag1
>         flag2
>         ...
>         flagn)
> 
> each flag has an associated command line switch (a string) and
> I have a list of mappings from string to 'set function', ie
> 
> (defconst flag-names
>         '(("-flag1"     flags-flag1)
>           ("-flag2"     flags-flag2)
>           ....))
> 
> now what I need to do is scan  the command line args (a list
> of strings) looking for matches and then set the associated flag
> in a global variable. The code similar to,
> 
> (setf action (find-flag flag-string))   ;; locate the matching flag
> ;; action will be flags-flag1 etc
> (eval `(setf (,action *flags*) t))


> which I think is what I aught to be doing. I'd like some kind
> of macro which expands to the right thing, but can't see how
> to do this without evaluating the result of the macro expansion.
Thanks to both Thomas A. Russ and Barry Margolin, I've come up with
a better way. Once I realised what I wanted was something that expanded
at read time, I made a dispatch-macro reader

(eval-when (compile eval)
  (defun flag-set-reader (stream subchar arg)
    (declare (ignore subchar) (ignore arg))
    (let ((flag (read stream t nil t)))
      `(function (lambda (var val) (setf (,flag var) val)))))
  (set-dispatch-macro-character #\# #\! #'flag-set-reader))

and then my table becomes

(defconstant *cmd-line-args*
  '(("-O1" nil "collapse expressions" #,#!flags-collapse-exprs)
    ("-O2" nil "propagate values + O1"
      (#,#!flags-propagate-values #,#!flags-variable-substitute "-O1"))
   ...))

Note this form is slightly different to my example above, because
I'd elided some information which wasn't relevant to the problem
(help strings, multiple effects per flag).

Anyway the idea is that the #!symbol expands to
  (function (lambda (var val) (setf (symbol var) val)))

The #, then makes sure this is compiled correctly.
I think #, (hash comma) is correct, not #. (hash dot))

Incidentally, whilst doing this I examined some other of my code
which used an alist to map symbols to functions and I'd constructed
the alist using backtick.
(setf func (cdr (assoc tag
      `((tag1 . ,#'random-func) (tag2 . ,#'other-func) ... ))))

I think this is wrong, as the alist (a compile time constant) is then
created anew each time the assoc is evaluated. I changed this to

(setf func (cdr (assoc tag
	'((tag1 . #,#'random-func) (tag2 . #,#'other-func) ... ))))

Is this the right thing to be doing?

nathan

-- 
Nathan Sidwell                    The windy road is more interesting
Chameleon Architecture Group at SGS-Thomson, formerly Inmos
http://www.pact.srf.ac.uk/~nathan/                  Tel 0117 9707182
······@pact.srf.ac.uk or ······@bristol.st.com
From: Rainer Joswig
Subject: Re: table of setf-methods
Date: 
Message-ID: <joswig-ya023180000901971903320001@news.lavielle.com>
In article <·············@pact.srf.ac.uk>, Nathan Sidwell
<······@pact.srf.ac.uk> wrote:

How about using SETF SLOT-VALUE?

Or how about using DEFCLASS with writer methods (no more setf!!!).
From: Kelly Murray
Subject: Re: table of setf-methods
Date: 
Message-ID: <5b6jb2$kca@sparky.franz.com>
> now what I need to do is scan  the command line args (a list
> of strings) looking for matches and then set the associated flag
> in a global variable. The code similar to,

> Thanks to both Thomas A. Russ and Barry Margolin, I've come up with
> a better way. Once I realised what I wanted was something that expanded
> at read time, I made a dispatch-macro reader

I would not use reader macros if they aren't really necessary.

As stated, the solution is very simple!

(defvar *flag-state* (make-hash-table :test #'equal))

(defun set-flag-p (flag value) (setf (gethash flag *flag-state*) value)

(defun flag-p (flag) (gethash flag *flag-state*))

(defun process-flags (flag-list)
  (clrhash *flag-state*)
  (loop for flag in flag do (setf (gethash flag *flag-state*) t))
   )

This solution can be extended to add more information with a flag,
but as stated, all you asked for was "flag-p" really.

-Kelly Murray   ···@franz.com