defadvice相比advice-add有何不足?

起因是purcell的一个commit:

然后我去看了一下各大配置,发现确实defadvice不那么受待见……

这是为什么?

谢谢各位

2 个赞

新的㝍法直观易懂易用

我现在是两种都用……毕竟advice-add “形式上”感觉有点多此一举……(必须为此专门写个函数,而不是写在一个sexp里面)

我用advice-add仅仅是在advice函数不止用于一处时……

1.

假设有函数:

(defin foo (bar quux)
  ...)

要求对其 around “修饰”一下。

新的 advice-add 写法:

(defun foo@around (fn bar quux)
  (funcall fn bar quux))

(advice-add 'foo :around 'foo@around)

旧的 defadvice 写法:

(defadvice foo (around foo@around (bar quux) activate)
  ad-do-it)

虽然少写了一行,但是,是不是觉得 ad-do-it 有点可疑?怎么又多了一个 activate?其实它就相当于 advice-add,所以真正对等的写法是:

(defadvice foo (around foo@around (bar quux))
  ad-do-it)

(ad-active 'foo)

这不就也是两步走了吗?而且还有个让我觉得比较错乱的是 (defadvice foo (around foo@around 这句声明到底谁在 around 谁?也许你说写多了就习惯了,好吧。

2.

接下来再加点需求:处理原函数的参数和返回值

新的写法:

(defun foo@around (fn bar quux)
  (let* ((new-bar ...)
         (new-quux ...)
         (result (funcall fn new-bar new-quux)))
    (... process the `result' ...)))

很直观,就是一般函数的写法。

旧的写法:

需要用到一堆可疑的 ad- 开头的宏,根本不想写。

3.

另外再补充一点,新的写法其实也可以一步到位:

(define-advice ...)
9 个赞

楼上的内容其实总结起来就是:

新的写法,就是一个普通函数的写法,你所需资源都声明好了,没有隐藏秘密。旧的写法,你需要记住很多额外的东西。


看看 `nadvice.el’ 文档是怎么说的:

划重点:with much fewer bells and whistles

2 个赞

感谢!

话说doom-emacs几乎全是advice-add……

以及define-advice的参数lambda-list是什么鬼?

……还有为什么那个commit里几乎所有的advice函数里都有一个&rest _参数?

因为advice函数会收到被advice函数的所有参数,如果你不关心这些参数就用&rest _,大部分语言都用_表示不关心的参数,而且Emacs byte compiler会无视_,不会因为你没用参数报unused variable警告。

(defun myfunc (a b c)
  (list a b c c))

(defun myadvice@myfunc (&rest _)
  (print "side effect"))

(advice-add 'myfunc :before 'myadvice@myfunc)
nil

(myfunc 1 2 3)

"side effect"
(1 2 3 3)
4 个赞

好吧……知道了。

然而我发现define-advice用得很少……大部分人还是defun...advice-add的形式……

我能想到的原因就是define-advice对advice function的命名……还有对老Emacs版本的兼容问题……

@twlz0ne 不是说了为啥不用define-advice么,麻烦啊。

@twlz0ne 说的不是defadvice么……

哈哈哈抱歉,看错了

这个有点奇怪,平时大家都prefer lambda to defun,现在这里又prefer defun

主要还是它对advice function的命名……如果我有一个很多字符的函数名,要给它加一个advice,那么advice function的名字就可能变成这样:

cua--deactivate-rectangle@suspend-whole-line-or-region-local-mode

而且它的语法也令人费解(比如参数LAMBDA-LIST是什么鬼?)。

最重要的一点可能是它只兼容25以后的Emacs版本。而从defadvice到advice-add的转换很多人都是在24的时代就做了。

所以就是……只有一点好处(代码比较简洁)但有很多不便的事情我为什么要做?

顺便一提它的展开式是

(prog1
    (defun ...)
  (advice-add ...))

可能还会拖慢那么一丁点点点的性能(毕竟等于多套了个prog1)。

=========================

但是我这种强迫症患者当然是会全部都用define-advice的,除了那个advice function本身还被别的地方引用,或者我要添加的advice function是个lambda函数……

请教个问题,原来用 defadvice 的时候,可以在 before 的时候 (ad-set-arg i v) 来改变变量的值,那在 (advice-add) 的时候,又该怎么做呢?

比如原来可以这样改变值

(defadvice xx (before xx-a (a b) activate)
  (ad-set-arg 0 1))

那现在用不了 ad-set-arg 的时候,该怎么做呢?

before 的时候改参数是为了传递给谁?根本没有接收者嘛。

around 的时候才有改参数的必要。新的写法无需改参数,直接传递新的值作为参数就可以可:

;; old style
(defadvice xx (around xx-a (a b) activate)
  (ad-set-arg 0 1)
  ...)
  
;; new style
(defun xx-a (orig-fn a b) 
  (let ((new-a 1))
    (funcall orig-fn new-a b)))
1 个赞

原来的那个 before 的是来自于 spacemacs 的 Chinese layer 里的!不过,现在我明白怎么弄了!感谢!

所以啊,老的写法早该废弃,新的写法清晰易懂。

我画个时序图来说明 before 和 around 的区别,以及为什么 before(或 after)无必要修改参数:

              +-------+          +------+
              | Emacs |          | User |
              +-------+          +------+
                  |                 |
                  |                 |
(funcall ad@before a b)             |
                  | --------------> |
                  |              (defun ad@before (a b)
                  |                ...)
                  | <-------------- |
  (funcall orig-fn a b)             |
                  |                 |
                  |                 |
                  v                 v


              +-------+          +------+
              | Emacs |          | User |
              +-------+          +------+
                  |                 |
                  |                 |
(funcall ad@around orign-fn a b)    |
                  | --------------> |
                  |              (defun ad@around (orig-fn a b)
                  |                (funcall orig-fn a b))
                  | <-------------- |
                  |                 |
                  |                 |
                  v                 v
9 个赞

确实清晰了很多,解决了我不少疑惑。