OpenMCL Common Lisp出现“无MAKE-LOAD-FORM”错误

7
我正在尝试在OS X上使用SLIME和OpenMCL(现在称为CCL)运行Paul Graham的《ANSI Common Lisp》中的光线追踪代码。 在该代码中,有一个常量定义其值为一个结构体,当我调用任何使用该常量的函数的slime-compile-and-load-fileslime-compile-defun时,会收到错误消息:
“没有为#S(POINT:X 0:Y 0:Z 200)定义MAKE-LOAD-FORM方法[类型为SIMPLE-ERROR]。”
我发现了一篇文章解释了这个问题,还有另一篇文章对此进行了抱怨,但是需要添加什么代码来解决OpenMCL的这个方面?
1个回答

8
当STRUCTURE-OBJECTs(以及其他一些类型的对象)在被COMPILE-FILE处理的代码中以文字、常量对象的形式出现时,COMPILE-FILE需要知道如何安排,当加载生成的二进制文件时,会创建一个“等效”的对象。 “等效”的定义有很多种:有时,加载的对象的组件与其他对象共享结构很重要;有时,初始化以某种方式发生很重要;有时,这些事情都不重要。为了确定如何重新创建常量对象,COMPILE-FILE调用通用函数MAKE-LOAD-FORM;这种行为应该在任何CL参考或教程中描述。(参考或教程还应指出,实现不能定义适用于所有STRUCTURE-CLASS或STANDARD-CLASS实例的默认MAKE-LOAD-FORM方法,并且应注意MAKE-LOAD-FORM-SAVING-SLOTS是用于简化初始化的对象的MAKE-LOAD-FORM方法的方便函数,例如:

(defmethod make-load-form ((p point) &optional env)
  (declare (ignore env))
  (make-load-form-saving-slots p))

请注意,该方法必须在编译时定义,以便COMPILE-FILE调用它来确定如何保存常量POINT对象。
这一切都与CCL无关。 可能的问题是哪些是常数,文字对象,哪些不是。
例如代码:
(defconstant a-point (make-point :x 0 :y 0 :z 200))

(defun return-a-point () a-point)

编译器可以(但不是必须)将 A-POINT 的值替换为函数 RETURN-A-POINT 中对它的引用。(如果编译器这样做了,那么意味着代码中有一个文字/常量 POINT 对象,COMPILE-FILE 需要调用 MAKE-LOAD-FORM 来确定对象应该如何保存和加载;如果编译器没有进行此替换,则在此示例中不需要调用 MAKE-LOAD-FORM。)
实现是否进行此类替换取决于实现。规范还未指定 DEFCONSTANT 表单中的值形式是在编译时、加载时还是两者都进行评估,并指出用户必须小心确保表达式始终评估为相同的值。
CCL 通常尝试在编译时评估 DEFCONSTANT 值形式,并且非常积极地将命名常量的值替换为对它们的引用;在某些情况下,这意味着必须定义常量值类的 MAKE-LOAD-FORM 方法。其他实现可能不愿意对某些类型的对象进行此替换。这两种策略都是正确的,可移植代码不能假定正在遵循哪些策略(尽管许多所谓的可移植代码肯定会做出这样的假设)。
DEFCONSTANT 定义的事物的不同处理似乎是此类问题(未经定义的 MAKE-LOAD-FORM 调用)的主要原因。可以通过以下方式避免其中一些问题,这种方式应该是可移植的:
(defconstant a-point (make-point :x 0 :y 0 :z 200))

(defun return-a-point () (load-time-value (symbol-value 'a-point)))

这将产生与允许实现(如CCL)进行常量替换类似的效果,但使用LOAD-TIME-VALUE将确保仅在加载时评估常量值(并且不涉及MAKE-LOAD-FORM)。

我猜你的DEFCONSTANT表达式中应该调用RETURN-A-POINT函数吧? - Elias Mårtenson

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接