Emacs 字节编译时警告:函数';空白模式';可能未在运行时定义

Emacs 字节编译时警告:函数';空白模式';可能未在运行时定义,emacs,elisp,Emacs,Elisp,如果我将以下内容放入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-

如果我将以下内容放入
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
当然,可以在运行时取消定义
空白模式
,但除非成功加载空白,否则不应执行该模式。如果加载空白,则将定义
空白模式


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

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

(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)))

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

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

与其使用
require
,不如使用
autoload
。然后您可以忘记所有关于
require
ing的内容,因为emacs(和字节编译器)将知道如何在首次调用函数时加载它

(autoload 'whitespace-mode "whitespace" nil t)
如果您知道函数将被加载,可以使用
declare function
告诉字节编译器某个函数是在某个文件中定义的。语法是
(声明函数文件ARGLIST)
。就你的情况来说

(declare-function whitespace-mode "whitespace" (&optional ARG))
我通常把这些放在使用它们的defun之前,但这只是为了组织目的


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

我在这里添加了我在上面作为评论的回应,只是为了记录在案


编译器说的是,函数是在编译时定义的,但在运行代码时它可能不存在(它告诉您在运行代码之前必须使用它)。因此,只有当您只需要在该包中定义宏时,才可以在编译时
求值。如果您还需要函数和/或变量,您应该只
需要
包,而不是在编译时在
eval下
,而是始终如此。

我经常想知道这一点。虽然从未遇到过任何问题,但无知有时是一种幸福。只是一个更新:从Emacs24.4开始,我没有看到这段代码上的警告;我认为编译器现在更聪明了。我已经在我的实际代码中这样做了,但我想了解发生了什么。我担心添加显式检查可能会隐藏我的代码的实际问题。如果在运行时没有定义函数,您可能确实会遇到问题。警告就是这么说的。它的发布是因为编译器在编译时不知道函数“whitespacemode”,所以他只是向您指出,您最好确保它存在。如果按如下方式进行字节编译:
emacs-Q--batch--eval“(需要'whitespace nil t)(字节编译文件\'foo.el\”)
,那么编译器在编译时知道函数,不会发出警告。Thomas,我还是不明白。如果你明确地说
(编译时求值(需要“空白”)
,为什么编译器在编译时不知道空白模式?好的,我想我现在明白了。编译器说的是,函数是在编译时定义的,但在运行代码时它可能不存在(它告诉您在运行代码之前必须使用它)。因此,只有当您只需要在该包中定义宏时,才可以在编译时
求值。如果您还需要函数和/或变量,则只需
require
程序包,而不是在编译时
eval下,而是始终。但在运行时它是
require
d;请注意操作中的
(当(需要'whitespace nil t)…
时)。在调用该函数时,该函数应始终可用,因为Emacs能够成功加载
空白
功能。因此,要么我有一个非常微妙的bug,要么编译器无法理解如果要调用该函数,该函数将保证可用。我想知道哪个。是的,它在运行时是必需的,这就是为什么我告诉你只要
require
(编译时不带
eval
),当你需要该函数在编译时和运行时都可用时。还要考虑到
时的
是一个宏,因此它可能不会计算其参数,因此编译器实际上无法知道将要生成的require。我希望它在编译时可用,以使警告静音,但在运行时是可选的(因此
时的
nil t
)。如果我总是需要
空白
,那么在安装时如果没有
空白
,它就会中断。你关于
何时
成为宏的观点很有趣。但是,当我执行
(当(fboundp'空白模式)
时,编译器不会打印警告,因此编译器必须了解
何时执行
。对吧?我想这只是因为当
是一个宏时。然后编译器无法猜测何时对其求值(如果有)。这会使警告静音,但不会回答我的问题。我担心代码中有一个bug。如果有bug,使用
declare函数将屏蔽它。如果没有bug,那么编译器为什么要打印警告?哦,对不起,我误解了。代码中没有bug,这是字节编译器的一个限制。它无法知道
require
ing
whitespace
将定义函数
空白模式