Können Funktionen auf ihren Namen zugreifen?

25

In C gibt es die magische Variable __func__, die den aktuellen Funktionsnamen enthält. In Bash gibt es ein Array FUNCNAME, das die Namen aller Funktionen im aufrufenden Stack enthält !!!

Gibt es in Emacs Lisp etwas Ähnliches? Oder eine einfache Möglichkeit für eine Funktion, auf ihren Namen zuzugreifen?

Ich habe im Emacs Lisp-Handbuch keine Antwort gefunden (Kapitel 12 über Funktionen oder Index von Variablen und Funktionen und ..)

phs
quelle
2
Wofür brauchst du das eigentlich?
Lunaryorn
1
Es ist nicht gerade eine magische Variable - es ist eine Präprozessordefinition, die von einigen Compilern eingefügt wird .
Sean Allred

Antworten:

6

Für interaktive Funktionen, dh Befehle, können Sie die Variable verwenden this-commandoder sicherer real-this-command. Der Unterschied besteht darin, dass Sie beim Schreiben Ihrer eigenen Funktionen den Wert von explizit ändern können this-command. Meistens wird dies getan, um Streiche mit last-commandsich wiederholenden Befehlen zu spielen. Das solltest du nicht (kannst du nicht?) Mit machen real-this-command. Es wird immer der Name des aktuellen Befehls sein.

Mir ist kein Äquivalent für nicht interaktive Funktionen bekannt.

Tyler
quelle
3
Es ist wichtig zu beachten, dass this-commandund real-last-commandüberhaupt nicht mögen __func__. Wenn beispielsweise der Befehl A den Befehl B aufruft, der ihn ausgibt this-command, wird der Befehl A und nicht B ausgegeben, und dies funktioniert überhaupt nicht für Funktionen.
Jordon Biondo
1
@JordonBiondo Einverstanden. Ich habe festgestellt, dass es für Funktionen nicht funktioniert. phs hat uns seinen kontext nicht mitgeteilt, deshalb habe ich vermutet, dass dies für seine anwendung gut genug sein könnte. Ihre Antwort ist robuster, keine Frage.
Tyler
22

Aktualisierte Antwort mit Suche nach Erweiterungszeit:

Ich sagte in meiner ursprünglichen Antwort, dass es eine Möglichkeit geben könnte, dies zur Expansions- / Kompilierungszeit anstatt zur Laufzeit zu tun, um eine bessere Leistung zu erzielen, und implementierte dies schließlich heute, während ich an meiner Antwort für diese Frage arbeitete: Wie kann ich bestimmen, welche Funktion war? interaktiv im Stack aufgerufen?

Hier ist eine Funktion, die alle aktuellen Backtrace-Frames liefert

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

Wenn wir das in einem Makro verwenden, können wir im Erweiterungsstapel nachsehen, welche Funktionsdefinition gerade erweitert wird, und diesen Wert direkt in den Code einfügen.

Hier ist die Funktion, um die Erweiterung durchzuführen:

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

Hier ist es in Aktion.

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

Ursprüngliche Antwort:

Mit können Sie backtrace-frameden Stapel nachschlagen, bis Sie einen Frame sehen, der einen direkten Funktionsaufruf darstellt, und den Namen daraus erhalten.

(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

Hier mache ich die Suche nach Funktionsnamen zur Laufzeit, obwohl es wahrscheinlich möglich ist, dies in einem Makro zu implementieren, das direkt in das Funktionssymbol erweitert wird, das für wiederholte Aufrufe und kompiliertes Elisp leistungsfähiger wäre.

Ich habe diese Information gefunden, als ich versucht habe, eine Art Funktionsaufruf-Logger für elisp zu schreiben, der hier in unvollständiger Form zu finden ist, aber er könnte für Sie nützlich sein. https://github.com/jordonbiondo/call-log

Jordon Biondo
quelle
Danke für den Code. Dies löst mein Problem und ich werde es in ein paar Tagen akzeptieren, es sei denn, jemand gibt uns eine Art "Name der aktuellen Funktion" -Variable, die Teil des Debuggers ist, auf den Sie hinweisen.
Phs
Übrigens scheint es nicht zu funktionieren, wenn a von defuneingeschlossen ist eval-and-compile, dh es kehrt zurück nil.
Alexander Shukaev