2
votes

I'd like to control the way the values are saved in slots and what is returned when I read a slot. Here is my class definition:

(defclass object ()
  ((name :accessor name-access
         :initform 'noname
         :initarg :name)
   (value :accessor value-access
      :initform 10
      :initarg :value)))

I create the object this way:

(setf obj1 (make-instance 'object))

This is the way how I get the value of the slot name:

(name-access obj1)

And how I set a new value:

(setf (name-access obj1) 'realname)

What is the right way to override this accessor function (or method) in order to be able to make some changes to the object (on write) and to control the returned value?

Thank you.

2

2 Answers

5
votes

You can just manually define the methods for getting and setting the slots:

(defclass foo ()
  ((name :initform 'noname
         :initarg :name)))

(defgeneric name-access (foo)
  (:method ((foo foo))
    (format t "~&Getting name.~%")
    (slot-value foo 'name)))

(defgeneric (setf name-access) (name foo)
  (:method (name (foo foo))
    (format t "~&Setting a new name.~%")
    (setf (slot-value foo 'name) name)))

(defparameter *foo* (make-instance 'foo))
(name-access *foo*)
; Getting name.
;=> NONAME

(setf (name-access *foo*) 'some-name)
; Setting a new name.
;=> SOME-NAME

(name-access *foo*)
; Getting name.
;=> SOME-NAME

The book Practical Common Lisp goes through these in chapter 17. You should read that.

5
votes

You can extend the accessor methods defined by DEFCLASS:

CL-USER 66 > (defclass object ()
               ((name :accessor name-access
                      :initform 'noname
                      :initarg :name)
                (value :accessor value-access
                       :initform 10
                       :initarg :value)))
#<STANDARD-CLASS OBJECT 4220014953>

Writing, using a :before method:

CL-USER 67 > (defmethod (setf name-access) :before (new-value (o1 object))
               (print "hi"))
#<STANDARD-METHOD (SETF NAME-ACCESS) (:BEFORE) (T OBJECT) 40202283BB>

Reading, with an :around method:

CL-USER 68 > (defmethod name-access :around ((o1 object))
               (let ((name (call-next-method)))
                 (values name (length (symbol-name name)))))
#<STANDARD-METHOD NAME-ACCESS (:AROUND) (OBJECT) 4020061213>

Example:

CL-USER 69 > (let ((o1 (make-instance 'object)))
               (setf (name-access o1) 'foobar)
               (name-access o1))

"hi"     ; side effect
FOOBAR   ; return value 1
6        ; return value 2