Access to the CLOS object slots from the external package used

I am studying the structure of my CL program and now have problems using CLOS when programming in general with packages.

package.lisp

(defpackage :my-project.a (:use :cl) (:export create-my-object my-object ; EXPORT SINGLE SLOTS? my-slot-1 ; my-slot-... ; my-slot-n ; OR EXPORT ALL ACCESSOR-FUNCTIONS? my-slot-1-accessor ; my-slot-n-accessor... )) (defpackage :my-project.b (:use :cl :my-project.a) (:export print-object-slot)) 

src.lisp

So far, the MY-OBJECT class is defined in MY-PROJECT.A.

 (in-package :my-project.a) (defclass my-object () ((my-slot-1 :accessor my-slot-1-accessor :initarg :my-slot-1) ;... more slots ; (my-slot-2 :accessor my-slot-2-accessor :initarg :my-slot-2) ; (my-slot-n :accessor my-slot-n-accessor :initarg :my-slot-n) )) 

like some CREATOR function for objects

 (defun create-my-object () (make-instance 'my-object :my-slot-1 "string" ;; further slots... )) 

The presence of some function, for example. PRINT-OBJECT in package MY-PROJECT.B, which must process the object called by the function

 (in-package :my-project.b) (defun print-object-slot (slot-name object) (format nil "slot-value: ~a" (SLOT-VALUE object slot-name))) 

Problem

When executing the following code does not work

 (in-package :my-project.b) (describe 'my-object) ; works (print-object-slot 'my-slot-1 ; while this works: 'my-project.a:my-slot-1 [if slot is exported] (create-my-object)) ;; ==> slot MY-PROJECT.B:MY-SLOT-1 is missing from the object ;; MY-PROJECT.A:MY-OBJECT 

To access my slots programmatically, in this situation I will need to combine the original package name with the name of the slot in order to get / install the slot from external classes ...

My understanding

Accessory functions from CLOS objects are common functions that belong to the package where they were defined through DEFCLASS, in this case: MY-PROJECT.A

In (use-package :my-project.a) in MY-PROJECT.B the exported characters are imported, so DESCRIBE works. But the symbols of universal slot functions are not included.

  • Consideration: The architecture of the program should NOT be planned for sharing / exporting objects and accessing slots. It is not well designed for mass import / export of slots / accessories-functions.

  • Consideration: You can create a custom function that receives / sets slots through the slot access function inside your package, so there is only one interface function for export?

My question is:

This way of handling external CLOS objects does not seem to work. How to export / import these access functions in a reasonable way without manually listing each individual slot?

Edit / Solution

My terminology and the use of slots or function accessories are the cause of this problem ( many thanks to @RainerJoswig for cleaning up the terminology ).

I have not used the exported version of the MY-SLOT-1-ACCESSOR function, which will work as expected, but I will need a "bulk export" if I would like to have access to all the slots in every other external package. @sds did an excellent job of showing how to do this, as well as pointing out the general problem of my approach . Thank you very much:)

In my opinion, I wanted to export only the object and get full access to all internal functions. But this is the wrong way for CLOS, since characters and methods do not share direct bindings to the class / object, and I need to adapt the best organization of the code.

+5
source share
3 answers

Export all accessories

You can use MOP to get a list of readers for your class as well, and then export all of them using

like this:

 (dolist (slot (class-direct-slots (find-class 'your-class-name))) (dolist (reader (slot-definition-readers slot)) (export reader))) 

Why is it so hard?

Because you do not want to do this.

All code that needs indiscriminate access to all the slots of the class must be in the same package as the class.

The only characters you export should be the ones you must export, and they must be explicitly verified by you.

+5
source

Terminology

The question does not distinguish between a slot, a slot, and a slot access function. Conflicting slot names and access functions is not a good idea. You must be clear what is what.

 (defpackage "GUI" (:use "CL") (:export ;; class window window-screen window-width window-height)) (defclass window () ((screen :accessor window-screen :initarg :screen) (width :accessor window-width :initarg :width :initform 640) (height :accessor window-height :initarg :height :initform 400))) 

Now screen is the name of the slot, and window-screen is the access function.

The name of the slot is just a symbol. You can use any character for this. For example, you can also write (just a random example, not to use):

 (defpackage "SLOTS" (:use)) (defpackage "AC" (:use) (:export "WINDOW-SCREEN" "WINDOW-WIDTH" "WINDOW-HEIGHT")) (defclass window () ((slots::screen :accessor ac:window-screen :initarg :screen) (slots::width :accessor ac:window-width :initarg :width :initform 640) (slots::height :accessor ac:window-height :initarg :height :initform 400))) 

Above, they will use the names of the slots in the slots package and the accessors in the ac package.

Accessor is a common function.

So when you write:

 (defun foo (instance slot-name) ...) 

I would expect the slot name to be a symbol, not an access function.

 (defun foo (instance accessor) ...) 

For the above, I would expect the accessory to be a function, not a symbol.

If you really want to make the difference clear, you can write methods:

 (defmethod foo (instance (path symbol)) (slot-value instance path)) (defmethod foo (instance (path function)) (funcall function instance)) 

What to export?

Usually I would like to export the names of accessories into a package, but not the names of the slots.

Import

But often I didn’t even import the package:

 (defpackage "GUI-GAME" (:use "CL")) 

The above package does not import the gui package. It may be, but here it is not.

 (defmethod describe-window ((w gui:window)) (format t "~% Window width:~a height:~a" (gui:window-width w) (gui:window-width h))) 

The advantage is that I see two things in the source code:

  • gui:window exported and therefore is part of the package interface
  • gui:window is actually from the gui package and there is no name conflict with other characters.

Just use the characters for the class and accessor function with the package names added.

+8
source

The print-object-slot function tries to call a function called literaly named slot-name , not a function named by the variable slot-name . You want to use funcall here.

 (defun print-object-slot (slot-name object) (format nil "slot-value: ~a" (funcall slot-name object))) 
0
source

Source: https://habr.com/ru/post/1262595/


All Articles