Могут ли функции получить доступ к их имени?

25

В C есть магическая переменная, __func__которая содержит текущее имя функции. В Bash есть массив, FUNCNAMEсодержащий имена всех функций в стеке вызовов !!!

Есть ли похожая вещь в Emacs Lisp? Или какой-нибудь простой способ для функции иметь доступ к своему имени?

Я не нашел никакого ответа в руководстве по Emacs Lisp (глава 12, посвященная функциям или индексу переменных и функций и ...)

PHS
источник
2
Зачем вам это нужно?
lunaryorn
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 Согласен. Я заметил, что это не работает для функций. phs не дал нам своего контекста, поэтому я догадался, что этого может быть достаточно для его применения. Ваш ответ более надежный, без вопросов.
Тайлер
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», которая является частью отладчика, на который вы намекаете.
тел.
Между прочим, это не похоже на работу, когда a defunзаключено eval-and-compile, то есть возвращается nil.
Александр Шукаев