ELisp:转发引用(在定义之前引用符号)

时间:2012-09-26 23:17:00

标签: emacs reference elisp

你如何解决这个问题?

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

我尝试了什么:

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

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

3 个答案:

答案 0 :(得分:4)

这个怎么样:

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

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

  

如果符号的函数单元格为空,则发出无效函数错误信号。

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

答案 1 :(得分:4)

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

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

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

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

  1. 它将在加载路径中查找库。
  2. 如果找到,将检查该功能是否已定义。
  3. 如果找不到库,它会认为库就在那里并且没有错误地传递。
  4. 因此,如果没有X库,它就不会抱怨,但如果有一个并且它没有定义函数,那么它就会。这意味着如果库的更新版本不包含X-function,那么您将知道何时尝试重新编译代码。

    如果你查看declare-function的文档,你会发现它也可以检查函数的参数列表。

    顺便说一下,如果您收到有关未声明变量的类似警告,可以使用以下方法禁止这些:

    (defvar X-variable)
    

    但是,即使您知道库将其设置为什么值,也不要设置变量,因为这可能会在以后的版本中发生变化。

    这为您提供了一个版本的程序,无论X库是否存在,该程序都有效。您可能更喜欢有两个版本,一个用于存在X库,另一个用于何时不存在。这可以通过宏来完成:

    (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库进行编译,则会扩展为对X-function的调用。如果库不存在则会扩展为空。在任何情况下,您都不需要声明函数。这给出了两个不同的版本,但它应该更有效,因为关于库是否存在的决定是在编译时而不是运行时进行的。

    一个小警告。如果您选择第二个解决方案,则必须在X库环境中或在其外部编译整个程序。如果您尝试在程序中途加载库,那么在解释时,它将按照您在加载前后不同扩展的宏的方式工作。但是在编译的程序中,宏只扩展一次。该库的测试测试是在代码中进行扩展而不是在扩展中,因此宏在加载之前和之后将不会起作用。

答案 2 :(得分: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 的情况下,第一次循环时,咬合编译器在看到函数调用时不知道该函数是否存在。在编译结束时,它知道函数存在,但它不够聪明,无法弄清楚它是如何知道的,所以你会得到一个不同的警告。但是,如果您在同一个Emacs会话中重新编译它,它确实知道警告就会消失。
  4. 请注意, eval-and-compile ,如 eval-with-compile ,对于Emacs解释器看起来像 progn