从使用过的外部包

时间:2017-01-10 17:50:39

标签: common-lisp packages clos

我正在学习构建我的CL程序,现在在使用包编程时使用CLOS时遇到了麻烦。

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

虽然在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工作的原因。但是不包括通用槽访问器功能的符号。

  1. 代价: 不应计划程序的体系结构来共享/导出对象和插槽访问。它没有很好地设计用于批量导入/导出插槽/访问器功能。

  2. 代价: 你可以构建一个自定义函数,它通过包中的slot-accessor-function来获取/设置插槽,所以只有一个接口函数可以导出?

  3. 我的问题:

    这种方式处理外部CLOS对象似乎不太可行。 如何以理智的方式导出/导入这些访问者功能,而不是手动列出每个插槽?

    编辑/解决方案

    我的终结和使用插槽与访问器功能是导致此问题的原因(非常感谢@RainerJoswig清除术语)。

    我确实没有使用MY-SLOT-1-ACCESSOR功能的导出版本,这可以按预期工作,但是需要我去"批量导出"他们,如果我想访问所有其他外部包中的所有插槽。 @sds做了很好的工作来展示如何做到这一点,并指出我的方法的一般问题。非常感谢:))

    在我看来,我希望只导出对象并获得对所有内部函数的完全访问权限。但这对于CLOS来说是错误的方式,因为符号和方法不能共享对类/对象的直接绑定,我必须调整更好的代码组织。

3 个答案:

答案 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)))