我认为用 setf
/cl-letf
是更靠谱的做法:
;;; example.el
(eval-and-compile
(defmacro mcr (&rest body)
`(list "a" ,@body))
(defalias 'mcr-orig
`,(symbol-function 'mcr))
(defmacro adv (&rest body)
`(mcr-orig "z" ,@body))
(setf (symbol-function 'mcr) 'adv))
;;; example-test.el
(require 'cl-lib)
;; 修改之后
(cl-assert (equal '("a" "z" b) (mcr 'b)))
;; 原始效果
(cl-assert (equal '("a" b) (mcr-orig 'b)))
;; 临时还原
(cl-assert (equal '("a" b) (cl-letf (((symbol-function 'mcr) 'mcr-orig))
(mcr 'b))))
相关主题:
NOTE: 如果宏在 setf
/cl-letf
之前就已于某个函数内使用,并且这个函数已编译,那么宏的行为也不会改变:
;; example.el
(eval-and-compile
(defmacro mcr (&rest body)
`(list "a" ,@body))
(defalias 'mcr-orig
`,(symbol-function 'mcr))
(defmacro adv (&rest body)
`(mcr-orig "z" ,@body))
(require 'cl-lib)
(defun call-mcr ()
(let ((byte-compiled-p (byte-code-function-p (symbol-function 'call-mcr))))
(message "==> byte-compiled-p: %s" byte-compiled-p)
(cond
;; 已编译,行为不变
(byte-compiled-p (cl-assert (equal '("a" b) (mcr 'b))))
;; 未编译,行为改变
(t (cl-assert (equal '("a" "z" b) (mcr 'b)))))))
(setf (symbol-function 'mcr) 'adv))
$ emacs -Q --batch -l /tmp/example.el -f call-mcr
==> byte-compiled-p: nil
$ emacs -Q --batch -l /tmp/example.elc -f call-mcr
==> byte-compiled-p: t