【问题标题】:Warning when byte compiling: the function 'whitespace-mode' might not be defined at runtime字节编译时警告:运行时可能未定义函数“空白模式”
【发布时间】:2012-01-17 22:28:16
【问题描述】:

如果我将以下内容放入foo.el

(defun whitespace-redraw ()
  (eval-when-compile (require 'whitespace nil t))
  (when (require 'whitespace nil t)
    (if whitespace-mode
        (whitespace-mode 0))
    (whitespace-mode 1)))

然后字节编译它:

emacs -Q --batch --eval '(byte-compile-file "foo.el")'

我收到以下警告:

In end of data:
foo.el:7:1:Warning: the function `whitespace-mode' might not be defined at
    runtime.
Wrote foo.elc

当然whitespace-mode 可以在运行时未定义,但除非空格加载成功,否则它不应该被执行。如果加载了空白,则将定义 whitespace-mode

这个警告是 emacs 编译器限制的结果还是我做错了什么?

【问题讨论】:

  • 我一直在想这个问题。不过从来没有遇到过任何问题,无知有时是幸福的。
  • 只是一个更新:从 Emacs 24.4 开始,我看不到此代码上的警告;我认为编译器现在更聪明了。

标签: emacs elisp


【解决方案1】:

我在这里添加我在上面作为评论发表的回复,仅供记录。

编译器所说的是该函数是在编译时定义的,但在您运行代码时它可能不存在(它告诉您在运行代码时必须事先要求它)。因此,如果您只需要在该包中定义的宏,eval-when-compile only 可能是一个好习惯。如果您还需要函数和/或变量,您应该只使用 require 包,而不是在 eval-when-compile 下,但总是这样。

【讨论】:

  • 但它 required 在运行时;注意 OP 中的 (when (require 'whitespace nil t) ...)。在调用函数时,函数应该始终可用,因为 Emacs 能够成功加载 whitespace 特性。因此,要么我有一个非常微妙的错误,要么编译器无法理解如果要调用该函数,该函数保证可用。我想知道哪个。
  • 是的,它在运行时是必需的,这就是为什么我告诉你只需要require(没有eval-when-compile)当你需要该函数在编译时和运行时都可用时时间。还要考虑到 when 是一个宏,所以它可能不会评估它的参数,所以编译器实际上无法知道将要生成 require。
  • 我希望它在编译时可用以消除警告,但在运行时是可选的(因此 whennil t)。如果我总是需要whitespace,那么它会在没有whitespace 的安装中中断。
  • 您关于when 是一个宏的观点很有趣。但是我做(when (fboundp 'whitespace-mode) ...)的时候编译器并没有打印警告,所以编译器一定对when有一定的了解。对吗?
  • 我认为这只是因为when 是一个宏。然后编译器无法猜测它何时会被评估(如果有的话)。
【解决方案2】:

编辑:在这种情况下,您的代码中没有错误,只是字节编译器不知道函数的定义位置。它不知道whitespace-mode 是在whitespace.el 中定义的,因为没有什么可以告诉它的。 (不)幸运的是,它还没有你那么聪明。 :-)

通常最好使用autoload,而不是使用require。然后你可以忘记所有关于requireing 的事情,因为 emacs(和字节编译器)会知道如何在第一次调用函数时加载它。

(autoload 'whitespace-mode "whitespace" nil t)

如果你知道函数已经被加载,你可以使用declare-function 告诉字节编译器在某个文件中定义了一个函数。语法为(declare-function FUNCTION FILE ARGLIST)。对于你的情况,它会是

(declare-function whitespace-mode "whitespace" (&optional ARG))

我通常将这些放在使用它们的 defun 之前,但这只是出于组织目的。

当然,如果您输入的内容不是真的(文件未定义函数),这些中的任何一个都可以掩盖错误,但我想没有办法解决这个问题,因为whitespace.el 可能会被删除或更改并且加载会失败。不过,我认为在大多数情况下不值得防范。

【讨论】:

  • 这会使警告静音,但不能回答我的问题。我担心代码中存在错误。如果有错误,使用declare-function 将掩盖它。如果没有错误,那么为什么编译器会打印警告?
  • 哦,对不起,我误会了。您的代码中没有错误,这是字节编译器的限制。它无法知道requireing whitespace 将定义一个函数whitespace-mode,所以它告诉你它可能没有被定义。当然,你我都知道whitespace-mode 是在文件whitespace 中定义的,而declare-function 所做的只是告诉字节编译器。添加declare-function 可以 掩盖错误,但您不必经常使用它。实际上,您可能应该使用autoload——我已经更新了我的答案。
【解决方案3】:

您可以在代码中添加显式检查:

(defun whitespace-redraw ()
  (eval-when-compile (require 'whitespace nil t))
  (when (and (require 'whitespace nil t)
             (fboundp 'whitespace-mode))
      (if whitespace-mode
          (whitespace-mode 0))
      (whitespace-mode 1)))

但这可能会在一段时间后变得很麻烦。

【讨论】:

  • 我已经在我的实际代码中做到了这一点,但我想了解发生了什么。我担心添加显式检查可能会隐藏我的代码的实际问题。
  • 好吧,如果函数没有在运行时定义,你可能确实会遇到问题。这就是警告所说的。发布它是因为在编译时编译器不知道函数“空白模式”,所以他只是向您指出,您最好确保它存在。如果你像这样字节编译:emacs -Q --batch --eval "(require 'whitespace nil t) (byte-compile-file \"foo.el\"))" 那么编译器在编译时就知道函数并且不会发出警告。
  • 托马斯,我还是不明白。如果你明确地说(eval-when-compile (require 'whitespace)),编译器为什么在编译时不知道空格模式?
  • 好的,我想我现在明白了。编译器说的是该函数是在编译时定义的,但是在您运行代码时它可能不存在(它告诉您在运行代码时必须事先要求它)。因此,如果您只需要在该包中定义的宏,eval-when-compile only 可能是一个好习惯。如果您还需要函数和/或变量,则应该只使用 require 包,而不是在 eval-when-compile 下,但始终如此。
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 2011-09-28
  • 2020-03-05
  • 2011-07-15
  • 2017-01-08
  • 1970-01-01
  • 1970-01-01
  • 2010-11-28
相关资源
最近更新 更多