如果我想要 advise 的函数包含一个 optional argument,我的 advice 不需要对 argument 做任何处理,这 advice 应该怎样定义呢?
直接照抄 arg list。
别闹
老的 defadvice 能处理这种情况吗?
跟被advice的一样就行了,如果是around的话多一个,第一个是原函数
那 advice 里面怎么样 call 原函数?
apply 还是 funcall ?
就是的啊…或者我们没理解你的意思?
随你了,都可以
好的,我写一个 example 试试
;; "+p 从系统剪切板paste时会调到此处
;; 如果在mac 终端下使用emacs ,则使用pbpaste从clipboard 获取内容
(defadvice gui-backend-get-selection (around get-clip-from-terminal-on-osx activate)
ad-do-it ;;call 原函数
(when (and (equal system-type 'darwin)
(not (display-graphic-p))
(not (window-system))
(equal (ad-get-arg 0) 'CLIPBOARD)) ; 获取参数
(let ((default-directory "~/"))
(setq ad-return-value (shell-command-to-string "pbpaste")))))
多谢各位,照抄列表是可行的,调用原始函数时要用 funcall
(defun myfun (&optional arg)
(interactive)
(if arg
(message "arg is %s" arg)
(message "No arg")))
(defun myadvice (orig-func &optional arg)
(message "from advice")
(funcall orig-func arg))
(advice-add #'myfun :around #'myadvice)
Advice 函数的参数表的写法没有要求,只要最后执行的时候参数数目不冲突就行,比如 &rest args
是万能的。
比如 :around
函数的调用规则是
(lambda (&rest r) (apply FUNCTION OLDFUN r))
^^^^^^^^
比如这样一个函数
(defun foo (a &optional b &rest c)
(list a b c))
如果要定义用在 :around
位置的 Advice 函数的话,下面的参数表写法都可行:
(old-fun a &optional b &rest c)
(old-fun a &rest r)
(old-fun &rest r)
(&rest r)
至于用哪一个就看你的需求和喜好了,比如假设你关心 old-fun
和第一个参数 a
,用
(old-fun a &rest _)
最简洁。
10# 楼 答案不对吧。
不处理 argument 就不需要抄,用 apply 调用原函数:
#+BEGIN_SRC emacs-lisp :results value
(defun myfun (arg1 arg2)
(list arg1 arg2))
(defun myadvice (fn &rest rest)
(apply fn rest))
(advice-add 'myfun :around 'myadvice)
(myfun "foo" "bar")
#+END_SRC
#+RESULTS:
| foo | bar |
处理一部分参数,就抄一部分:
#+BEGIN_SRC emacs-lisp :results value
(defun myfun (arg1 arg2)
(list arg1 arg2))
(defun myadvice (fn arg1 &rest rest)
(let ((arg1 "qux"))
(apply fn arg1 rest)))
(advice-add 'myfun :around 'myadvice)
(myfun "foo" "bar")
#+END_SRC
#+RESULTS:
| qux | bar |
其实严格说来,不处理也可以照抄。只不过,既然不处理,就无需费力抄。而且抄了还要维护,以后原函数参数变更,advice 参数也要变更,这不自找麻烦吗。所以啊,&rest 应万变。
有三个参数,最后一个 &rest,只要处理第二个怎么办?
那也只能抄到第二个参数了:
#+BEGIN_SRC emacs-lisp :results value
(defun myfun (arg1 arg2 &rest rest)
(list arg1 arg2 rest))
(defun myadvice (fn _ arg2 &rest rest)
(let ((arg2 2))
(apply fn _ arg2 rest)))
(advice-add 'myfun :around 'myadvice)
(myfun "a" "b" "c" "d"))
#+END_SRC
#+RESULTS:
| a | 2 | (c d) |
用参数表分解或者在函数体中分解:
(defun foo (a b &rest c) (list a b c))
;; => foo
(foo 1 2 3 4)
;; => (1 2 (3 4))
(define-advice foo (:override (_ b &rest _) only-b)
(list b))
;; => foo@only-b
(foo 1 2 3 4)
;; => (2)
(define-advice foo (:override (&rest args) only-b)
(let ((b (cl-second args)))
(list b)))
;; => foo@only-b
(foo 1 2 3 4)
;; => (2)
这两种写法都有效并非魔法,只是因为你定义的函数会被 apply
调用的缘故。
考虑这样一个问题或许有助于理解:问什么的参数表能同时满足以下四种情况?
(apply FUNC '())
(apply FUNC '(1))
(apply FUNC '(1 2))
(apply FUNC '(1 2 3))
以下四种都行
&optional a b c
&optional a b &rest r
&optional a &rest r
&rest r
为什么(advice-remove 'foo #'foo@only-b)
之后(foo 1 2 3 4)
结果还是(2)
,就好像advice-remove
失败了一样?
建议用advice-add
,define-advice
已经deprecate了
已经改掉了,应该是我复制粘贴中间又执行了代码。define-advice
同名函数会自动重新执行定义,不用 advice-remove
也行,所以索性去掉了。
define-advice
是 Emacs 25.1 才加入了,它的效果类似于 defun
+ advice-add
。
你说的是 defadvice
吧,虽然已经 deprecated 但这个函数还是使用最多的:
⋊> ag defadvice elpa-27.0.50/ -l | wc -l
40
⋊> ag define-advice elpa-27.0.50/ -l | wc -l
0
⋊> ag advice-add elpa-27.0.50/ -l | wc -l
23