+(defmacro define-protocol-class
+ (name alien-type superclasses slots &rest options)
+ (let ((to-protocol (intern (format nil "ALIEN-TO-~A" name)))
+ (to-alien (intern (format nil "~A-TO-ALIEN" name))))
+ `(progn
+ (defclass ,name ,superclasses
+ ,(loop for slotd in slots
+ collect (ldiff slotd (member :array-length slotd)))
+ ,@options)
+ (declaim (inline ,to-alien ,to-protocol))
+ (defun ,to-protocol (alien &optional instance)
+ (declare (type (sb-alien:alien (* ,alien-type)) alien)
+ (type (or null ,name) instance))
+ (unless instance
+ (setf instance (make-instance ',name)))
+ ,@(loop for slotd in slots
+ ;; FIXME: slotds in source are more complicated in general
+ ;;
+ ;; FIXME: baroque construction of intricate fragility
+ for array-length = (getf (cdr slotd) :array-length)
+ if array-length
+ collect `(progn
+ (let ((array (make-array ,array-length)))
+ (setf (slot-value instance ',(car slotd))
+ array)
+ (dotimes (i ,array-length)
+ (setf (aref array i)
+ (sb-alien:deref
+ (sb-alien:slot alien ',(car slotd))
+ i)))))
+ else
+ collect `(setf (slot-value instance ',(car slotd))
+ (sb-alien:slot alien ',(car slotd))))
+ instance)
+ (defun ,to-alien (instance &optional alien)
+ (declare (type (or null (sb-alien:alien (* ,alien-type))) alien)
+ (type ,name instance))
+ (unless alien
+ (setf alien (sb-alien:make-alien ,alien-type)))
+ ,@(loop for slotd in slots
+ for array-length = (getf (cdr slotd) :array-length)
+ if array-length
+ collect `(progn
+ (let ((array (slot-value instance ',(car slotd))))
+ (dotimes (i ,array-length)
+ (setf (sb-alien:deref
+ (sb-alien:slot alien ',(car slotd))
+ i)
+ (aref array i)))))
+ else
+ collect `(setf (sb-alien:slot alien ',(car slotd))
+ (slot-value instance ',(car slotd)))))
+ (find-class ',name))))
+