git.fiddlerwoaroof.com
Raw Blame History
#.(progn (ql:quickload :closer-mop) nil)

(defpackage :fwoar.simple-clone
  (:use :cl )
  (:export #:cloneable-class
           #:make-from))
(in-package :fwoar.simple-clone)

(defclass cloneable-class (c2mop:standard-class)
  ((%parent :initform (make-hash-table :test 'eq :weakness :key)
            :reader parent)))

(defmethod c2mop:validate-superclass ((class cloneable-class) (superclass c2mop:standard-class))
  t)

(defmethod c2mop:slot-boundp-using-class ((class cloneable-class) instance slotd)
  (or (call-next-method)
      (alexandria:when-let ((next (gethash instance (parent class))))
        (slot-boundp next
                     (c2mop:slot-definition-name slotd)))))

(defmethod c2mop:slot-value-using-class ((class cloneable-class) instance slotd)
  (block nil
    (handler-bind ((unbound-slot
                     (lambda (c)
                       (declare (ignore c))
                       (alexandria:when-let* ((next (gethash instance (parent class)))
                                              (value (slot-value next
                                                                 (c2mop:slot-definition-name slotd))))
                         (return (setf (c2mop:slot-value-using-class class instance slotd)
                                       value))))))
      (call-next-method))))

(defun make-from (a &rest r &key &allow-other-keys)
  (let* ((the-class (class-of a))
         (result (apply #'make-instance the-class r)))
    (prog1 result
      (setf (gethash result (parent the-class)) a))))

(defun relate (base derive)
  (let ((dict (parent (class-of derive))))
    (setf (gethash derive dict)
          base)))

#+(or)
(progn
  (defclass base ()
    ((a :initarg :a :reader a)
     (b :initarg :b :reader b)
     (c :initarg :c :reader c)))

  (defclass test ()
    ((a :initarg :a :reader a)
     (b :initarg :b :reader b)
     (c :initarg :c :reader c))
    (:metaclass cloneable-class))

  (defmethod print-object ((o test) s)
    (with-accessors ((a a) (b b) (c c)) o
      (print-unreadable-object (o s :type t :identity t)
        (handler-bind ((unbound-slot (lambda (c) (use-value :unbound c))))
          (format s "(a: ~s b: ~s c: ~s)"
                  (with-simple-restart (use-value "skip slot ~s" 'a)
                    a)
                  (with-simple-restart (use-value "skip slot ~s" 'b)
                    b)
                  (with-simple-restart (use-value "skip slot ~s" 'c)
                    c)))))))