我在定义 advice 时发现,有时会报错 wrong-number-of-arguments
比如:
(defun some-advice (orig-func &rest args)
do-something
)
(advice-add #'some-function :after #'some-advice)
这时如果把 some-advice 的 orig-func 参数去掉就可以通过,但是有的时候这样就没有任何问题。
我的问题是,为什么两种情况下 orig-func
都没有在 advice 函数体中被调用,但有时候可以通过,有时候又不能呢?
我是不是可以这样认为,通常只要我不调用 orig-func
也就是原始函数,就不要加这个参数? @xuchunyang
我测试了一下。大概就是这样。
而且用defadvice
的话会自动继承原函数的参数列表。
1 个赞
(defun say(something)
(message "say(args => \"%s\")" something))
(defun after-say (&rest args)
(message "after-say(args => \"%s\")" args))
(defun after-say2 (f &rest args)
(message "after-say2(f => %s, args => \"%s\")" f args))
(advice-add 'say :after #'after-say)
(advice-add 'say :after #'after-say2)
(say "foo")
;; output:
;; say(args => "foo")
;; after-say(args => "(foo)")
;; after-say2(f => foo, args => "nil")
:after
得到的只是原函数的参数。:around
才可以有 orig-func
。
3 个赞
但是 before 貌似可以有 orig-func?因为我运行 before 的时候没有报错
我知道怎么回事了,多谢指点! @twlz0ne
我的使用情形中(这里没有放我的代码,上面问题中的只是伪代码):
因为我 before advise 的函数本身可以跟参数,所以就没有报错;
但是 after advise 的函数本身不能跟参数,所以报错了。
但是并不意味着我 before advise 那样的用法就是正确的,因为 orig-func 在 before 中并不代表原始函数 。
@twlz0ne 的办法很好,善用 message 函数真的很重要。
虽然你 :before
那样写了,但是不调用 orign-func
应该就不会出错。
我认为 :before
/:after
只是用 :around
在特定场景下的简便写法,它在 :before/:after
前/后已经执行过原始函数了,没必要传递过去再执行一遍。
是的,多谢指点。现在心里跟明镜似的,终于解开了我的疑惑。
et2010
2017 年3 月 20 日 07:03
10
我发现这样 advice 也有不好的地方,比如 advice 的位置没有 defadvice 中那样容易指定,刚开始容易记错 100 -100 innermost outermost
什么的很费解。当然这样的好处就是掌握以后会比 defadvice 更强大,细节也更完善了。
et2010
2017 年3 月 20 日 07:05
11
需要纠正的一点是,如果你 advise 的原始函数本身不能跟参数,那么即使你没有调用 orig-func
,一样会报错。
依据文档 (elisp) Advice combinators :after
的调用方式是
(lambda (&rest r) (prog1 (apply OLDFUN r) (apply FUNCTION r)))
也就是说你的 some-advice
要与 some-function
的参数保持一致(不一定要完全一样,满足 apply
的调用规则就行)。其它的位置 :around
:after
等的调用规则不清楚的话,使用前看下文档。
1 个赞
advice 的参数调用的问题,我之前也不明白,误以为所有位置的规则是一样的,糊里糊涂试了很久也没有搞懂,后来看下文档立马就明白了。
使用哪一种参数形式主要还是看用途吧,比如:
(defun div(a b)
(/ a b))
(defun before-div1(&rest args)
"不关心参数是什么")
(defun before-div2(a b)
"检查除数是否大于0"
(if (>= 0 b)
(message "除数必须大于 0")))
(advice-add 'div :before #'before-div1)
(advice-add 'div :before #'before-div2)
(div 2 0)
例子可能不太恰当,毕竟不能阻止原函数继续执行。