6

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.

Rainer Joswig
  • 136,269
  • 10
  • 221
  • 346
aitorpazos
  • 164
  • 1
  • 6

2 Answers2

5

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)

Edit:

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.

huaiyuan
  • 26,129
  • 5
  • 57
  • 63
  • Using: (funcall(fdefinition (list 'setf (intern "STRUCT-ESTADISTICAS-NUM-1"))) 0 *estadisticas*) I still get: Error: `(SETF STRUCT-ESTADISTICAS-NUM-1)' is not fbound – aitorpazos Sep 04 '10 at 11:40
  • 1
    don't use INTERN. Use FIND-SYMBOL, maybe check if the symbol is found and also make sure that the accessor is looked up in the correct package. – Rainer Joswig Sep 04 '10 at 13:49
  • 2
    That's non-standard, I'd say. DEFSTRUCT is not required to define WRITER functions. – Rainer Joswig Sep 07 '10 at 07:15
5

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.

Rainer Joswig
  • 136,269
  • 10
  • 221
  • 346
  • In possible solution b), it may yield an infinitely recursive function, if the Lisp implementation actually generates setf functions for structure slot accessors. – acelent Jun 25 '14 at 18:12