Subject: Re: Enumerations
From: (Rob Warnock)
Date: Sat, 10 Nov 2007 04:56:14 -0600
Newsgroups: comp.lang.lisp
Message-ID: <>
R. Matthew Emerson <> wrote:
| Don Geddis <> writes:
| > I had a sudden idea: maybe I should just use variables that
| > evaluate to integers:
| >         (in-package "ENUM")
| >         (defparameter elementary 0)

DEFCONSTANT would probably be more appropriate here than DEFPARAMETER.

CMUCL does the same thing a lot in the UNIX package when defining bits
needed for system calls, e.g., from "cmucl-19c/src/unix.lisp":

    (defconstant o_rdonly 0 "Read-only flag.") 
    (defconstant o_wronly 1 "Write-only flag.")
    (defconstant o_rdwr   2 "Read-write flag.")

| OpenMCL has a DEFENUM macro that's used in a few places in the system.
| It's used in circumstances like this:
| (ccl::defenum (:prefix "MXCSR-" :suffix "-BIT")
|   ie                                    ;invalid exception
|   de                                    ;denormal exception
|   rc1                                   ;rounding control bit 1
|   fz                                    ;flush-to-zero (not-IEEE)
| )

CMUCL has a DEF-ENUM in the UNIX package that has less flexibility
in naming [the :prefix & :suffix args in OpenMCL's DEFENUM are a nice
touch here] but a *lot* more flexibility in auto-assigning values.
The macro is:

    (defmacro def-enum (inc cur &rest names)
      (flet ((defform (name)
		 (prog1 (when name `(defconstant ,name ,cur))
		   (setf cur (funcall inc cur 1)))))
	`(progn ,@(mapcar #'defform names))))

Notice that "INC CUR" values of "ASH 1" are useful for bitmasks,
while "+ 0" is useful for sequential integers from 0:

    ;; Input modes. Linux: /usr/include/asm/termbits.h
    (def-enum ash 1 tty-ignbrk tty-brkint tty-ignpar tty-parmrk tty-inpck
	      tty-istrip tty-inlcr tty-igncr tty-icrnl #-bsd tty-iuclc
	      tty-ixon #-bsd tty-ixany tty-ixoff #+bsd tty-ixany
	      #+hpux tty-ienqak #+bsd nil tty-imaxbel)

    ;; output modes
    #-bsd (def-enum ash 1 tty-opost tty-olcuc tty-onlcr tty-ocrnl tty-onocr
			tty-onlret tty-ofill tty-ofdel)
    #+bsd (def-enum ash 1 tty-opost tty-onlcr)
    ;; special control characters
    #+(or hpux svr4 linux) (def-enum + 0 vintr vquit verase vkill veof
				     #-linux veol #-linux veol2)
    #+bsd (def-enum + 0 veof veol veol2 verase nil vkill nil nil vintr vquit)

The "(WHEN NAME ...)" in the definition lets you skip values, e.g.;

    > (unix::def-enum + 1 one two nil four nil nil nil eight)

    > (list eight four two one)

    (8 4 2 1)

The generality of the INC argument is somewhat marred by the fact
that the function has to have a macro-expansion-time value, so to
supply your own function you need to EVAL-WHEN it [in-line LAMBDAs
don't work here, since INC is passed *unevaluated* to FUNCALL]:

    > (eval-when (:compile-toplevel :load-toplevel :execute)
	(defun x3 (x ign)
	  (declare (ignore ign))
	  (* x 3)))

    > (unix::def-enum X3 1
	one three nine twenty-seven eighty-one two-hundred-forty-three
	seven-hundred-twenty-nine two-thousand-one-hundred-eighty-seven)

    > (list one three nine two-thousand-one-hundred-eighty-seven)

    (1 3 9 2187)


Rob Warnock			<>
627 26th Avenue			<URL:>
San Mateo, CA 94403		(650)572-2607