Actually, the cl-defstruct
macro does preserve enough info to make such a function possible, but I don't think anyone has written such a beast yet.
The way this info is stored has changed in Emacs-25: now the macro expands to various other things plus a call to cl-struct-define
which creates a class object (itself a struct). You can inspect it with C-h o cl-structure-class RET
(which is the corresponding metaclass) and C-h o cl-slot-descriptor RET
. And you can get the class object from the type name with (cl--find-class <type>)
.
You should even be able to extend make-instance
with a method to handle defstruct types. E.g.:
(cl-defmethod make-instance ((class symbol) &rest slots)
(let ((co (cl--find-class class)))
(if co (apply #'make-instance co slots)
(cl-call-next-method))))
(cl-defmethod make-instance ((sc cl-structure-class) &rest slots)
(let* ((sds (cl--struct-class-slots sc))
;; For techno-historical reasons, the first slot should be the
;; type/symbol rather than the class.
(o (make-record (cl--struct-class-name sc) (length sds) nil))
(alist '()))
(while slots
(push (cons (intern (substring (symbol-name (pop slots)) 1))
(pop slots))
alist))
(dotimes (i (length sds))
(pcase-let* (((cl-struct cl-slot-descriptor name initform)
(aref sds i)))
(aset o (1+ i) (or (alist-get name alist) initform))))
o))
Note: currently in Elisp, apply
can be a performance problem (e.g. it's the main bottleneck in cl-print-object
), so the above could be too slow for some use-cses.