Czy funkcje mogą uzyskać dostęp do swojej nazwy?

25

W C znajduje się zmienna magiczna, __func__która przechowuje aktualną nazwę funkcji. W Bash znajduje się tablica FUNCNAMEzawierająca nazwy wszystkich funkcji na stosie wywołującym !!!

Czy w Emacs Lisp jest coś podobnego? Lub jakikolwiek prosty sposób, aby funkcja miała dostęp do jej nazwy?

Nie znalazłem żadnej odpowiedzi w podręczniku Emacsa Lispa (rozdział 12 o funkcjach lub indeks zmiennych i funkcji i ..)

phs
źródło
2
Do czego tak naprawdę potrzebujesz?
lunaryorn
1
To nie jest dokładnie magiczna zmienna - to definicja preprocesora wstawiona przez niektóre kompilatory.
Sean Allred

Odpowiedzi:

6

Dla funkcji interaktywnych, to znaczy poleceń można użyć zmiennej this-command, lub bezpieczniejszymi, real-this-command. Różnica polega na tym, że pisząc własne funkcje, możesz wyraźnie zmienić wartość this-command. Przeważnie odbywa się to w celu zagrania trików last-commandzwiązanych z powtarzaniem poleceń. Nie powinieneś (nie możesz?) Tego zrobić real-this-command. Zawsze będzie to nazwa bieżącego polecenia.

Nie znam odpowiednika funkcji nieinteraktywnych.

Tyler
źródło
3
Ważne jest, aby to zauważyć this-commandi real-last-commandwcale tak nie jest __func__. Na przykład, jeśli polecenie A wywołuje polecenie B, które this-commandje wypisuje , wypisze polecenie A, a nie B, to również nie działa w ogóle dla funkcji.
Jordon Biondo
1
@JordonBiondo Zgoda. Zauważyłem, że to nie działa dla funkcji. phs nie podał nam swojego kontekstu, więc domyślałem się, że może to być wystarczające dla jego aplikacji. Twoja odpowiedź jest bardziej wiarygodna, bez pytania.
Tyler,
22

Zaktualizowana odpowiedź z wyszukiwaniem czasu rozszerzenia:

Powiedziałem w mojej oryginalnej odpowiedzi, że może istnieć sposób, aby to zrobić w czasie rozszerzania / kompilacji zamiast w czasie wykonywania, aby zapewnić lepszą wydajność, i w końcu zaimplementowałem to dzisiaj, pracując nad moją odpowiedzią na to pytanie: Jak ustalić, która funkcja była wywoływany interaktywnie na stosie?

Oto funkcja, która zwraca wszystkie bieżące ramki śledzenia

(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)))

Korzystając z tego w makrze, możemy sprawdzić stos rozszerzeń, aby zobaczyć, która definicja funkcji jest rozwijana w danym momencie i umieścić tę wartość w kodzie.

Oto funkcja wykonania rozszerzenia:

(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)))))))

Tutaj jest w akcji.

(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'"

Oryginalna odpowiedź:

Możesz użyć backtrace-framedo sprawdzenia stosu, aż zobaczysz ramkę reprezentującą bezpośrednie wywołanie funkcji i uzyskaj z tego nazwę.

(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

W tym momencie wykonuję wyszukiwanie nazwy funkcji w czasie wykonywania, chociaż prawdopodobnie można to zaimplementować w makrze, które rozwija się bezpośrednio w symbol funkcji, który byłby bardziej wydajny w przypadku powtarzania wywołań i kompilacji elisp.

Znalazłem tę informację, próbując napisać rodzaj rejestratora wywołań funkcji dla elisp, który można znaleźć tutaj w niekompletnej formie, ale może być dla ciebie przydatny. https://github.com/jordonbiondo/call-log

Jordon Biondo
źródło
Dzięki za kod. To rozwiązuje mój problem i zaakceptuję go za kilka dni, chyba że ktoś poda nam jakąś zmienną „bieżąca funkcja-nazwa”, która jest częścią debugera, o którym sugerujesz.
phs
Nawiasem mówiąc, wydaje się, że nie działa, gdy a defunjest otoczone eval-and-compile, tzn. Zwraca nil.
Alexander Shukaev,