2

你如何解决这个问题?

假设我想编写一个执行以下操作的函数:如果用户安装了库 X,则使用函数 X-function,否则 - 跳过?

我尝试了什么:

(when (symbol-function 'X-function)
  (X-function))

我收到此代码的警告 - 那么正确的方法是什么?

4

3 回答 3

4

这个怎么样:

(when (fboundp 'X-function)
    (X-function))

http://www.gnu.org/software/emacs/manual/html_node/elisp/Function-Cells.html上的文档介绍了符号功能

如果符号的功能单元格是无效的,则会发出无效功能错误信号。

我猜这就是你所看到的。另一方面, fboundp 只是根据函数是否存在返回 t 或 nil。

于 2012-09-26T23:27:00.643 回答
4

抑制此编译器警告的方法是:

(declare-function X-function "ext:X-library.el")

(when (fboundp 'X-function)
     (X-function))

这里 X-library 是库存在时定义 X-function 的库的名称。然后字节编译器将执行以下操作:

  1. 它将在加载路径中查找库。
  2. 如果找到它,它将检查该函数是否已定义。
  3. 如果它没有找到库,它将假定它会在库在那里并且没有错误地传递。

因此,如果没有 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 环境中或外部编译整个程序。如果您尝试在程序中途加载库,那么当解释它时,它将按照您所期望的那样工作,宏在加载之前和之后扩展不同。但是在编译的程序中,宏只扩展一次。库的测试测试是在执行扩展的代码中而不是在扩展中,因此宏在加载之前和之后的工作方式不同。

于 2012-11-27T16:00:41.400 回答
0

另一种可能会收到无法找到函数的警告的情况是,您以编程方式定义函数并使用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 知道了以编程方式定义的函数。

  1. 对于function1,字节编译器会在 Emacs 评估表单后看到函数调用,因此您不会收到任何警告。
  2. function2的情况下,字节编译器永远不知道函数已定义,因此您总是会收到警告。
  3. function3的情况下,第一次,bite 编译器在看到函数调用时并不知道该函数存在。在编译结束时,它知道该函数存在,但它不够聪明,无法弄清楚它是如何知道的,因此您会收到不同的警告。但是,如果您在同一个 Emacs 会话中重新编译它,它确实知道,因此警告消失了。

请注意,eval-and-compileeval-with-compile类似,在 Emacs 解释器中看起来像是一个progn 。

于 2012-12-13T22:20:54.857 回答