我正在学习构建我的CL程序,现在在使用包编程时使用CLOS时遇到了麻烦。
(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))
虽然在MY-PROJECT.A
中定义了MY-OBJECT类(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)
))
作为对象的一些CREATOR函数
(defun create-my-object ()
(make-instance 'my-object
:my-slot-1 "string"
;; further slots...
))
有一些功能,例如包MY-PROJECT.B中的PRINT-OBJECT, 应该处理从函数
实例化的对象(in-package :my-project.b)
(defun print-object-slot (slot-name object)
(format nil "slot-value: ~a" (SLOT-VALUE object slot-name)))
执行以下代码时,无法正常工作
(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
要以编程方式访问我的插槽,在这种情况下,我需要将原始package-name与slot-name合并,以从外部类获取/设置插槽...
来自CLOS对象的访问器函数是属于包的通用函数,它们通过DEFCLASS定义,在这种情况下:MY-PROJECT.A
通过MY-PROJECT.B中的(use-package :my-project.a)
,导入导出的符号,这就是DESCRIBE工作的原因。但是不包括通用槽访问器功能的符号。
代价: 不应计划程序的体系结构来共享/导出对象和插槽访问。它没有很好地设计用于批量导入/导出插槽/访问器功能。
代价: 你可以构建一个自定义函数,它通过包中的slot-accessor-function来获取/设置插槽,所以只有一个接口函数可以导出?
这种方式处理外部CLOS对象似乎不太可行。 如何以理智的方式导出/导入这些访问者功能,而不是手动列出每个插槽?
我的终结和使用插槽与访问器功能是导致此问题的原因(非常感谢@RainerJoswig清除术语)。
我确实没有使用MY-SLOT-1-ACCESSOR功能的导出版本,这可以按预期工作,但是需要我去"批量导出"他们,如果我想访问所有其他外部包中的所有插槽。 @sds做了很好的工作来展示如何做到这一点,并指出我的方法的一般问题。非常感谢:))
在我看来,我希望只导出对象并获得对所有内部函数的完全访问权限。但这对于CLOS来说是错误的方式,因为符号和方法不能共享对类/对象的直接绑定,我必须调整更好的代码组织。
答案 0 :(得分:8)
<强>术语强>
问题不会使插槽,插槽名称和插槽访问器功能之间的区别变得清晰。将插槽名称和访问器功能限制在一起并不是一个好主意。你应该清楚什么是什么。
(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)))
现在screen
是广告位名称,window-screen
是访问者功能。
插槽名称只是一个符号。您可以使用任何符号。例如,你也可以写(只是一个随机的例子,不要使用):
(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)))
上面将使用包slots
中的插槽名称和包ac
中的访问者。
访问者是一种通用函数。
所以,当你写:
(defun foo (instance slot-name)
...)
我希望slot-name是一个符号,而不是一个访问函数。
(defun foo (instance accessor)
...)
对于上面我会期望访问者是一个函数,而不是一个符号。
如果你真的希望明确区别,你可以编写方法:
(defmethod foo (instance (path symbol))
(slot-value instance path))
(defmethod foo (instance (path function))
(funcall function instance))
要导出什么内容?
通常我会在包中导出访问者名称,但不会导出插槽名称。
导入吗
但通常我甚至不会导入包:
(defpackage "GUI-GAME"
(:use "CL"))
以上包不会导入包gui
。它可以,但在这里它没有。
(defmethod describe-window ((w gui:window))
(format t "~% Window width:~a height:~a"
(gui:window-width w)
(gui:window-width h)))
优点是我在源代码中看到两件事:
gui:window
,因此是包接口的一部分gui:window
实际上来自包gui
,并且没有名称
与其他符号冲突。只需使用类的符号和访问器函数及其包名前缀。
答案 1 :(得分:5)
您可以使用MOP获取读者列表 您的班级的作家,然后使用
导出所有这些作家 像这样:(dolist (slot (class-direct-slots (find-class 'your-class-name)))
(dolist (reader (slot-definition-readers slot))
(export reader)))
因为不想要这样做。
所有需要不加选择地访问类的所有插槽的代码 应该与班级在同一个包裹中。
您导出的唯一符号应该是您 要导出的符号,以及 他们应该由你明确审查。
答案 2 :(得分:0)
您的print-object-slot
函数正在尝试调用一个名为slot-name
的文件,而不是变量slot-name
命名的函数。您想在此处使用funcall
。
(defun print-object-slot (slot-name object)
(format nil "slot-value: ~a" (funcall slot-name object)))