2

The following snippet, featuring the "fantasy function" clone-function, illustrates what I'd like to do:

(defvar last-enabled-foo nil)

;; hold on to a "clone"/copy of third-party function enable-foo
(defvar original-enable-foo (clone-function 'enable-foo))

;; replace enable-foo with a wrapper function around it
;; module foo.el
(defun enable-foo (foo)
  (setq last-enabled-foo foo)
  (funcall original-enable-foo foo))

Basically, clone-function ensures that original-enable-foo refers to a function that is completely distinct from the newly defined enable-foo.

How could one implement clone-function?

1
7

For variety, here's a solution using the :around advice.

Copy the below test snippet to the *scratch* buffer and evaluate the progn form.

(progn
  (defvar last-enabled-foo nil)
  (setq last-enabled-foo nil)

  (defun enable-foo (foo)
    (message "last-enabled-foo = %S" last-enabled-foo))

  (defun adv/enable-foo (orig-fun &rest args)
    (setq last-enabled-foo args)
    (apply orig-fun args))
  (advice-add 'enable-foo :around #'adv/enable-foo)
  ;; Comment the below line to see the advice in effect
  (advice-remove 'enable-foo #'adv/enable-foo)

  (enable-foo 9))

With the way it is right now, you will always see the output as:

last-enabled-foo = nil

This is because we are setting the last-enabled-foo value to nil and the value 9 passed to enable-foo is not being assigned to last-enabled-foo.

(setq last-enabled-foo nil)
(defun enable-foo (foo)
  (message "last-enabled-foo = %S" last-enabled-foo))
(enable-foo 9)

The advice code is ineffective as we are adding an advice and then again removing the same.


Now comment out the (advice-remove ..) line. Now the advice will be in effect and you will see the magic when you re-evaluate that same progn form.

Now, the output will be:

last-enabled-foo = (9)

Now, even though the setq form is setting the last-enabled-foo value to nil during each progn evaluation, it is being set to the enable-foo argument args inside the advice function adv/enable-foo.

(defun adv/enable-foo (orig-fun &rest args)
  (setq last-enabled-foo args)
  (apply orig-fun args))

References:

4

I agree that using advice is probably the way to go, but if you really, truly have to, you can use something like this. (Comments are welcome.)

(defun third-party-func ()
  (message "original"))

(defalias 'third-party-func-original
          (symbol-function 'third-party-func))

(defun third-party-func ()
  (message "new!")
  (third-party-func-original))

; now (third-party-func) calls the patched version;
; (third-party-func-original) calls the original version
7
  • I'd use defalias instead of fset. – wasamasa Jan 27 '16 at 21:51
  • 1
    @wasamasa: Do you mind explaining why? (I just learned about defalias-fset-function and that defalias records which file defined the function (like defun, but I have no idea what the implications are.) – Constantine Jan 27 '16 at 21:55
  • 1
    It is all about communicating intent. The purpose of this example is having an alias of the original function around, so you'd use defalias instead of fset. If you were doing something like building your own flet, fset would be the more idiomatic choice. – wasamasa Jan 27 '16 at 22:13
  • @wasamasa: Yup. Good point. – Constantine Jan 27 '16 at 22:20
  • 1
    symbol-function isn't needed either as both fset and defalias operate on the function slot. – wasamasa Jan 30 '16 at 13:11

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy

Not the answer you're looking for? Browse other questions tagged or ask your own question.