你如何解决这个问题?
假设我想编写一个执行以下操作的函数:如果用户安装了库 X,则使用函数 X-function,否则 - 跳过?
我尝试了什么:
(when (symbol-function 'X-function)
(X-function))
我收到此代码的警告 - 那么正确的方法是什么?
这个怎么样:
(when (fboundp 'X-function)
(X-function))
http://www.gnu.org/software/emacs/manual/html_node/elisp/Function-Cells.html上的文档介绍了符号功能
如果符号的功能单元格是无效的,则会发出无效功能错误信号。
我猜这就是你所看到的。另一方面, fboundp 只是根据函数是否存在返回 t 或 nil。
抑制此编译器警告的方法是:
(declare-function X-function "ext:X-library.el")
(when (fboundp 'X-function)
(X-function))
这里 X-library 是库存在时定义 X-function 的库的名称。然后字节编译器将执行以下操作:
因此,如果没有 X 库,它不会抱怨,但如果有一个并且它没有定义函数,那么它会抱怨。这意味着如果库的更新版本不包含 X-function,那么您将知道何时尝试重新编译您的代码。
如果您查看 declare-function 的文档,您会发现它还可以检查函数的参数列表。
顺便说一句,如果您收到有关未声明变量的类似警告,您可以使用以下方法抑制这些警告:
(defvar X-variable)
但是,即使您知道库将其设置为什么值,也不要设置该变量,因为这可能会在以后的版本中发生变化。
这为您提供了一个版本的程序,无论 X-library 是否存在都可以使用。您可能更喜欢有两个版本,一个用于 X-library 存在时,一个用于不存在时。这可以通过宏来完成:
(defmacro run? (function &rest args)
"Expand to function call if function exists."
(when (fboundp `,function)
`(,function ,@args)))
现在而不是像这样的电话:
(X-function a1 a2 a3)
你写:
(run? X-function a1 a2 a3)
如果你用 X-library 编译它,这将扩展为对 X-function 的调用。如果该库不存在,则它会扩展为什么都没有。在任何情况下,您都不需要 declare-function。这提供了两个不同的版本,但它应该更有效,因为关于库是否存在的决定是在编译时而不是运行时做出的。
一个小警告。如果您选择第二种解决方案,您必须在 X-library 环境中或外部编译整个程序。如果您尝试在程序中途加载库,那么当解释它时,它将按照您所期望的那样工作,宏在加载之前和之后扩展不同。但是在编译的程序中,宏只扩展一次。库的测试测试是在执行扩展的代码中而不是在扩展中,因此宏在加载之前和之后的工作方式不同。
另一种可能会收到无法找到函数的警告的情况是,您以编程方式定义函数并使用fset 对其进行设置。以下示例说明了这一点以及如何处理它:
(eval-and-compile
(fset 'my-function1 (lambda () nil)))
(my-function1)
(fset 'my-function2 (lambda () nil))
(my-function2)
(my-function3)
(eval-and-compile
(fset 'my-function3 (lambda () nil)))
如果你编译它,你会得到警告:
Warning: the function `my-function2' is not known to be defined.
和:
Warning: the function `my-function3' might not be defined at runtime.
如果您在同一个 Emacs 会话中第二次重新编译代码,第二个警告就会消失,但第一个警告不会。
这里发生的是:当编译器看到eval-and-compile时,它首先评估当前 Emacs 会话中的主体,然后编译它。在评估了代码之后,Emacs 知道了以编程方式定义的函数。
请注意,eval-and-compile与eval-with-compile类似,在 Emacs 解释器中看起来像是一个progn 。