Чи можуть функції отримати доступ до їх імені?


25

В C є магічна змінна, __func__яка містить ім'я поточної функції. У Bash є масив, що FUNCNAMEмістить назви всіх функцій у стеку виклику !!!

Чи є в Emacs Lisp подібне? Або будь-який простий спосіб, щоб функція отримала доступ до свого імені?

Я не знайшов жодної відповіді в посібнику Emacs Lisp (глава 12 про функції або індекс змінних та функцій і ..)


2
Для чого вам насправді це потрібно?
місячник

1
Це не зовсім магічна змінна - це визначення попереднього процесора, вставлене деякими компіляторами.
Шон Алред

Відповіді:


6

Для інтерактивних функцій, тобто, команди, ви можете використовувати змінну this-commandабо безпечніше, real-this-command. Різниця полягає в тому, що, коли ви пишете власні функції, ви можете явно змінити значення this-command. В основному це робиться для гри в трюки, last-commandпов’язані з повторюваними командами. Ви не повинні (не можете?) Цього робити real-this-command. Це завжди буде ім'ям поточної команди.

Я не знаю еквівалента неінтерактивних функцій.


3
Важливо зауважити, що this-commandі real-last-commandзовсім не схожі __func__. Наприклад, якщо команда A викликає команду B, яка виводить this-commandїї, буде друкувати команду A, а не B, також це зовсім не працює для функцій.
Джордон Біондо

1
@JordonBiondo Погодився. Я зазначив, що це не працює для функцій. ph не дав нам його контексту, тому я здогадався, що це може бути досить добре для його застосування. Ваша відповідь є більш надійною, жодного питання.
Тайлер

22

Оновлена ​​відповідь із пошуком часу розширення:

У своїй оригінальній відповіді я сказав, що може бути спосіб зробити це під час розширення / компіляції замість часу запуску, щоб забезпечити кращу продуктивність, і я нарешті це реалізував сьогодні, працюючи над своєю відповіддю на це питання: Як я можу визначити, яка функція була називається інтерактивно в стеку?

Ось функція, яка дає всі поточні кадри відтворення

(defun call-stack ()
  "Return the current call stack frames."
  (let ((frames)
        (frame)
        (index 5))
    (while (setq frame (backtrace-frame index))
      (push frame frames)
      (incf index))
    (remove-if-not 'car frames)))

Використовуючи це в макросі, ми можемо шукати стек розширення, щоб побачити, яке визначення функції розширюється в той час, і поставити це значення прямо в код.

Ось функція робити розширення:

(defmacro compile-time-function-name ()
  "Get the name of calling function at expansion time."
  (symbol-name
   (cadadr
    (third
     (find-if (lambda (frame) (ignore-errors (equal (car (third frame)) 'defalias)))
              (reverse (call-stack)))))))

Ось воно в дії.

(defun my-test-function ()
  (message "This function is named '%s'" (compile-time-function-name)))

(symbol-function 'my-test-function)
;; you can see the function body contains the name, not a lookup
(lambda nil (message "This function is named '%s'" "my-test-function"))

(my-test-function)
;; results in:
"This function is named 'my-test-function'"

Оригінальний відповідь:

Ви можете backtrace-frameшукати стек до тих пір, поки не побачите кадр, який представляє прямий виклик функції та не отримаєте ім'я від цього.

(defun get-current-func-name ()
  "Get the symbol of the function this function is called from."
  ;; 5 is the magic number that makes us look 
  ;; above this function
  (let* ((index 5)
         (frame (backtrace-frame index)))
    ;; from what I can tell, top level function call frames
    ;; start with t and the second value is the symbol of the function
    (while (not (equal t (first frame)))
      (setq frame (backtrace-frame (incf index))))
    (second frame)))

(defun my-function ()
  ;; here's the call inside my-function
  (when t (progn (or (and (get-current-func-name))))))

(defun my-other-function ()
  ;; we should expect the return value of this function
  ;; to be the return value of my-function which is the
  ;; symbol my-function
  (my-function))

(my-other-function) ;; => 'my-function

Тут я роблю пошук імені функції під час виконання, хоча, ймовірно, це можливо реалізувати за допомогою макросу, який перетворюється безпосередньо на символ функції, який був би більш ефективним для повторних викликів та компільованого elisp.

Я знайшов цю інформацію під час спроби написати такий реєстратор викликів функцій для elisp, який можна знайти тут у неповній формі, але він може бути корисним для вас. https://github.com/jordonbiondo/call-log


Дякуємо за код. Це вирішує мою проблему, і я прийму її через кілька днів, якщо хтось не дасть нам якусь змінну "current-function-name", яка є частиною налагоджувача, на який ви натякаєте.
phs

До речі, він, здається, не працює, коли a defunукладений eval-and-compile, тобто повертається nil.
Олександр Шукаєв
Використовуючи наш веб-сайт, ви визнаєте, що прочитали та зрозуміли наші Політику щодо файлів cookie та Політику конфіденційності.
Licensed under cc by-sa 3.0 with attribution required.