ARNESI
(defmacro defclass-struct (name-and-options supers &rest slots)
"DEFCLASS with a DEFSTRUCT api.
NAME-AND-OPTIONS:
name-symbol |
( name-symbol [ (:conc-name conc-name ) ]
[ (:predicate predicate-name ) ] )
SUPERS - a list of super classes passed directly to DEFCLASS.
SLOTS - a list of slot forms:
name |
( name [ init-arg ] [ slot-options* ] )"
(generate-defclass (first (ensure-list name-and-options))
(cdr (ensure-list name-and-options))
supers slots))
(defun generate-defclass (class-name class-options supers slots)
(let ((conc-name nil)
(predicate nil)
(predicate-forms nil))
(loop
for (option-name . args) in class-options
do (ecase option-name
(:conc-name
(when conc-name
(error "Can't specify the :CONC-NAME argument more than once."))
(setf conc-name (first args)))
(:predicate
(when predicate
(error "Can't specify the :PREDICATE argument more than once."))
(setf predicate (if (eql t (first args))
(intern (strcat class-name :-p)
class-name)
(first args))))))
(setf slots
(mapcar
(lambda (slot-spec)
(destructuring-bind (name
&optional initform
&rest options)
(ensure-list slot-spec)
`(,name
:initform ,initform
,@(when conc-name
`(:accessor ,(intern (strcat conc-name name)
(symbol-package conc-name))))
:initarg ,(intern (symbol-name name) :keyword)
,@options)))
slots)
predicate-forms
(if predicate
(with-unique-names (obj)
`((defmethod ,predicate ((,obj ,class-name)) t)
(defmethod ,predicate ((,obj t)) nil)))
nil))
`(prog1
(defclass ,class-name ,supers ,slots)
,@predicate-forms)))