假设在库中定义了一个样本函数(这个问题的前提是这个库中的所有定义都不能被修改,比如“只读”):
(defun sample ()
(foo)
(bar)
(baz))
我想使用这个库,但函数sample
无法满足我的要求,我想要的是:
(defun sample ()
(foo)
(when condition
(bar))
(baz))
有人告诉我使用defadvice
,但我注意到defadvice
只能在sample
的调用之前或之后插入代码,例如:
(before-advice ...)
(sample)
(after-advice ...)
它无法修改sample
本身的定义。那么,我怎样才能慷慨地实现这一目标呢?我是否应该自己重写sample
,称为my-sample
或sample2
?
答案 0 :(得分:5)
sds的答案有效,除了您可能只想在bar
执行时建议sample
,所以您还需要建议样本以激活和停用{的建议{1}}。我的bar
宏有助于此:
with-temporary-advice
请注意,如果(defmacro with-temporary-advice (function class name &rest body)
"Enable the specified advice, evaluate BODY, then disable the advice."
`(unwind-protect
(progn
(ad-enable-advice ,function ,class ,name)
(ad-activate ,function)
,@body)
(ad-disable-advice ,function ,class ,name)
(ad-activate ,function)))
(defadvice bar (around my-conditional-bar disable)
;; This advice disabled by default, and enabled dynamically.
(when condition
ad-do-it))
(defadvice sample (around my-sample-advice activate)
"Make execution of `bar' conditional when running `sample'."
(with-temporary-advice 'bar 'around 'my-conditional-bar
ad-do-it))
在执行bar
时也以其他方式调用,则该建议也将适用于这些调用,因此如果可能,您应该考虑到这一点。
或者,您可能希望在需要时使用sample
重新定义flet
。当然,这与第一种解决方案一样需要注意。
bar
这是很多更容易阅读,但由于我不明白(defadvice sample (around my-sample-advice activate)
"Make execution of `bar' conditional when running `sample'."
(if condition
ad-do-it
(flet ((bar () nil))
ad-do-it)))
的原因,从Emacs 24.3开始,不再支持。它的文档字符串建议使用flet
,但由于cl-flet
使用词法绑定,这实际上不起作用。我能说的最好,听起来cl-flet
实际上并没有消失,但目前的建议似乎是使用建议。
另请注意,如果在flet
内部,不需要的行为取决于某些变量,那么最好对该变量使用bar
绑定而不是let
对函数的绑定。
修改强>
当然,这些方法确实让人更难看到正在发生的事情。根据具体情况,最好只需重新定义flet
函数来执行您想要的操作(或编写sample
函数来调用它,就像您建议的那样。)
答案 1 :(得分:4)
其他人已经提供了很好的答案,但由于有些人抱怨flet
的耻辱,我会展示我的用法:
(defvar my-inhibit-bar nil)
(defadvice bar (around my-condition activate)
(unless my-inhibit-bar ad-do-it))
(defadvice sample (around my-condition activate)
(let ((my-inhibit-bar (not condition)))
ad-do-it))
看吧!没有flet
并且没有丑陋的激活/无效!当你C-h f bar
时,它会清楚地告诉你,不只是满足于眼睛。另外,我实际上使用了新的advice-add
:
(defvar my-inhibit-bar nil)
(defun my-bar-advice (doit &rest args)
(unless my-inhibit-bar (apply doit args)))
(advice-add :around 'bar #'my-bar-advice)
(defun my-sample-advice (doit &rest args)
(let ((my-inhibit-bar (not condition)))
(apply doit args)))
(advice-add :around 'sample #'my-sample-advice)
答案 2 :(得分:3)
您应该使用bar
建议来建议功能around
:
(defadvice bar (around my-condition)
(when condition
ad-do-it))