I'd like to setf
different fields of a struct depending on a certain variable. I decided to use the following approach:
Generate a string with the field's accessor name:
(setq my-string (format nil "STRUCT-ESTADISTICAS-NUM-~S" x))
and then use intern with funcall:
(funcall (intern my-string) *estadisticas*)
This call returns the correct value of the struct's field, but if I try setf
to modify this value it complains saying:
(setf (funcall(intern my-string) *estadisticas*) 0)
Error: `(SETF FUNCALL)' is not fbound
I can understand why it doesn't work, but I can't find a way to modify the struct's fields. Any idea? Thank you.
You want to call a writer function of the struct via its name, and the name of the writer is the list (setf accessor-name)
; so
(funcall (fdefinition (list 'setf (intern my-string))) 0 estadisticas)
Not seeing the rest of your code, it's hard to fathom what went wrong. On SBCL this works for me:
(defstruct point x y)
(let ((point (make-point :x 1 :y 2)))
(funcall (fdefinition (list 'setf (intern "POINT-X"))) 10 point)
point)
The above evaluates to
#S(POINT :X 10 :Y 2),
as expected.
Motivation:
Structures are a relatively low-level facility. 'Speed' was an important design goal. Indirection via writer functions is not supported by the standard (as I read it). Today, use CLOS as the default, unless one needs better efficiency of structures (faster read and writes of slots with structures are sometimes possible).
First - style:
Don't use INTERN, use FIND-SYMBOL. Also specify the package, otherwise FIND-SYMBOL will use the runtime value of *package* as the package
Second - DEFSTRUCT
If I read the ANSI CL standard correctly, it is not that DEFSTRUCT creates writer functions for slots like DEFCLASS does.
CL-USER 24 > (defstruct foo bar baz)
FOO
CL-USER 25 > #'(setf foo-bar)
Error: Undefined function (SETF FOO-BAR) in form (FUNCTION (SETF FOO-BAR)).
So, constructing such a name (SETF FOO-BAR) and trying to find a function for that will fail, since there is no such function defined by the DEFSTRUCT.
That in user code (setf (foo-bar some-struct) 42) works, is based on defined SETF expansions provided by DEFSTRUCT, but not on defined SETF accessor functions.
Some Common Lisp implementations may provide writer functions as a non-standard extension to ANSI CL.
Possible solutions:
a) use CLOS classes, DEFCLASS does what you want
b) write the writer functions yourself
(defun (setf foo-bar) (new-value struct)
(setf (foo-bar struct) new-value))
Now:
(funcall (fdefinition '(setf foo-bar)) 300 *foo*)
Above then works.
c) (SETF SLOT-VALUE) - another non-standard feature of some implementations.
In some implementations of Common Lisp this works not only for CLOS classes, but also for structures:
(setf (slot-value some-struct 'bar) 42)
I'm not sure if Allegro CL does support that, but that would be easy to find out.
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With