3

With this:

(defclass test-class ()
  ((data :initarg :data)
   (other :initarg :other)))

(defmethod test ((tc test-class) &key)
  (macrolet ((print-slot (slot)
               `(with-slots (,slot) tc
                  (format t "slot value: ~a~%" ,slot))))
    (print-slot data)
    (print-slot other)))

I can do:

CL-USER> (test (make-instance 'test-class :data "data" :other "other"))
slot value: data
slot value: other

But I can't seem to iterate over them:

(defmethod test ((tc test-class) &key)
  (macrolet ((print-slot (slot)
               `(with-slots (,slot) tc
                  (format t "slot value: ~a~%" ,slot))))
    (dolist (slot '(data other))
      (print-slot slot))))
CL-USER> (test (make-instance 'test-class :data "data" :other "other"))
When attempting to read the slot's value (slot-value), the slot
SLOT is missing from the object #<TEST-CLASS {1001E3FFB3}>.
   [Condition of type SB-PCL::MISSING-SLOT]

Any idea how I can get this effect?

4
  • Macros evaluate their arguments at compile time, you can't use a run-time variable for it. Commented May 9 at 14:52
  • Use a function instead. Commented May 9 at 14:53
  • related: stackoverflow.com/questions/40742048/… Commented May 11 at 20:30
  • Is there a particular reason you wanted to use a macro? Commented May 12 at 7:54

2 Answers 2

3

Use a function, not a macro, so the argument will be evaluated.

(defmethod test ((tc test-class) &key)
  (flet ((print-slot (slot)
           (format t "slot value: ~a~%" (slot-value tc slot))))
    (dolist (slot '(data other))
      (print-slot slot))))
Sign up to request clarification or add additional context in comments.

Comments

2

Still without a macro, to iterate on all slots of a given object:

(defclass test-class ()
  ((data :initarg :data)
   (other :initarg :other)))

(defparameter *obj* (make-instance 'test-class :data "foo" :other "bar"))

(dolist (slot (sb-mop:class-slots (class-of *obj*)))
  (format t "this is slot ~a~&" slot))
;; this is slot #<STANDARD-EFFECTIVE-SLOT-DEFINITION DFIO:DATA>
;; this is slot #<STANDARD-EFFECTIVE-SLOT-DEFINITION LS-USER::OTHER>

You can inspect a slot object with describe:

#<SB-MOP:STANDARD-EFFECTIVE-SLOT-DEFINITION DATA>
  [standard-object]

Slots with :INSTANCE allocation:
  SOURCE                         = NIL
  NAME                           = DATA
  INITFORM                       = NIL
  INITFUNCTION                   = NIL
  INITARGS                       = (:DATA)
  %TYPE                          = T
  %DOCUMENTATION                 = NIL
  %CLASS                         = #<STANDARD-CLASS CL-USER::TEST-CLASS>
  ACCESSOR-FLAGS                 = 7
  INFO                           = #S(SB-PCL::SLOT-INFO..
  ALLOCATION                     = :INSTANCE
  ALLOCATION-CLASS               = #<STANDARD-CLASS CL-USER::TEST-CLASS>
  LOCATION                       = 0

and access its metadata with sb-mop:slot-definition-[name, initargs, allocation…]. Get their value with slot-value:

(slot-value *obj* (sb-mop:slot-definition-name slot))

See also class-direct-slots, and use closer-mop for portability.

1 Comment

I would say this is the right answer. sb-mop:class-slots lists by introspection the names of the slots over which you can iterate.

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.