编辑:在最近的Emacs中执行此操作的更好方法是通过定义编译器宏来检查参数数量。我使用普通宏的原始答案保留在下面,但是编译器宏比较好,因为它不会阻止将函数传递给运行时funcall
或apply
在运行时传递。
在最新版本的Emacs中,您可以通过为函数定义一个编译器宏来执行此操作,该宏检查参数的数量并在不匹配时产生警告(甚至是错误)。唯一的妙处是,编译器宏应返回未更改的原始函数调用形式,以进行评估或编译。这是通过使用&whole
参数并返回其值来完成的。可以这样完成:
(require 'cl-lib)
(defun my-caller (&rest args)
(while args
(message "%S %S" (pop args) (pop args))))
(define-compiler-macro my-caller (&whole form &rest args)
(when (not (cl-evenp (length args)))
(byte-compile-warn "`my-caller' requires an even number of arguments"))
form)
(my-caller 1 2 3 4)
(my-caller 1 2)
(funcall #'my-caller 1 2 3 4) ; ok
(apply #'my-caller '(1 2)) ; also ok
(my-caller 1) ; produces a warning
(funcall #'my-caller 1 2 3) ; no warning!
(apply #'my-caller '(1 2 3)) ; also no warning
需要注意的是funcall
和apply
现在可以使用,但它们绕过编译器宏参数检查。尽管他们的名字,编译器宏也似乎在通过的“解释”评估过程中进行扩展C-xC-e,M-xeval-buffer,所以你会得到的评估,以及对编译这个例子中的错误。
原始答案如下:
这是实现Jordon建议“使用在扩展时将提供警告的宏”的建议。事实证明这很容易:
(require 'cl-lib)
(defmacro my-caller (&rest args)
(if (cl-evenp (length args))
`(my-caller--function ,@args)
(error "Function `my-caller' requires an even number of arguments")))
(defun my-caller--function (&rest args)
;; function body goes here
args)
(my-caller 1 2 3 4)
(my-caller 1 2 3)
尝试在文件中编译以上内容将失败(不生成.elc
文件),并在编译日志中显示一个不错的可点击错误消息,内容为:
test.el:14:1:Error: `my-caller' requires an even number of arguments
您也可以替换为(error …)
,(byte-compile-warn …)
以产生警告而不是错误,从而允许继续编译。(感谢乔顿在评论中指出了这一点)。
由于宏是在编译时扩展的,因此与该检查无关的运行时代价。当然,您不能阻止其他人my-caller--function
直接呼叫,但是您至少可以使用双连字符约定将其宣传为“私有”功能。
为此目的使用宏的一个显着缺点是它my-caller
不再是一流的函数:您不能将其传递给运行时funcall
或apply
在运行时传递(或至少它不能满足您的期望)。在这方面,该解决方案不如能够简单地为实函数声明编译器警告那样好。当然,使用apply
它将使无法在编译时检查传递给函数的参数数量,因此也许这是一个可以接受的折衷方案。