(defun copy-instance (i &aux (i-class (class-of i)))
(let ((c (make-instance i-class)))
(loop for s in (mop:class-slots i-class)
do (setf (slot-value c (mop:slot-definition-name s))
(slot-value i (mop:slot-definition-name s)))
finally (return c))))
Where you see mop: look out. :) The MOP package of the CL implementation you are using may even be nicknamed CLOS. You may have to take the union of class-direct-slots and class-indirect-slots. The slot returned may be the slot name itself. Stuff like that.
(defun copy-instance (i)
(loop with i-class = (class-of i)
with c = (allocate-instance i-class)
for sd in (mop:class-slots i-class)
for sn = (mop:slot-definition-name sd)
when (slot-boundp i sn)
do (setf (slot-value c sn)
(slot-value i sn))
finally (return c)))
But what if necessary side-effects happen in initialize-instance? Though that could as easily make a case for allocate-instance, if Paul needs to avoid those side effects.
I think the bottom line is that I should have emphasized that copy-instance above might not survive first contact with the enemy (the functional requirements).