Les fonctions peuvent-elles accéder à leur nom?

25

En C, il y a la variable magique __func__qui contient le nom de la fonction actuelle. Dans Bash, il y a un tableau FUNCNAMEcontenant les noms de toutes les fonctions dans la pile d'appel !!!

Y a-t-il une chose similaire dans Emacs Lisp? Ou un moyen simple pour qu'une fonction ait accès à son nom?

Je n'ai trouvé aucune réponse dans le manuel Emacs Lisp (chapitre 12 sur les fonctions ou index des variables et fonctions et ..)

phs
la source
2
Pourquoi avez-vous réellement besoin de cela?
lunaryorn
1
Ce n'est pas exactement une variable magique - c'est une définition de pré-processeur insérée par certains compilateurs.
Sean Allred

Réponses:

6

Pour les fonctions interactives, par exemple, les commandes, vous pouvez utiliser la variable this-command, ou plus sûr, real-this-command. La différence étant que lorsque vous écrivez vos propres fonctions, vous pouvez explicitement changer la valeur de this-command. Généralement, cela est fait pour jouer des tours last-commandliés à la répétition de commandes. Vous ne devriez pas (ne pouvez pas) faire cela avec real-this-command. Ce sera toujours le nom de la commande en cours.

Je ne connais pas d'équivalent pour les fonctions non interactives.

Tyler
la source
3
Il est important de noter que this-commandet real-last-commandne sont pas du tout comme __func__. Par exemple, si la commande A appelle la commande B qui l'imprime, this-commandelle imprimera la commande A, pas B, cela ne fonctionne pas du tout pour les fonctions.
Jordon Biondo
1
@JordonBiondo D'accord. J'ai noté que cela ne fonctionne pas pour les fonctions. phs ne nous a pas donné son contexte, donc j'ai deviné que cela pourrait être assez bon pour son application. Votre réponse est plus robuste, sans aucun doute.
Tyler
22

Réponse mise à jour avec recherche de temps d'extension:

J'ai dit dans ma réponse d'origine qu'il pouvait y avoir un moyen de le faire au moment de l'expansion / de la compilation au lieu de l'exécution pour donner de meilleures performances et j'ai finalement implémenté cela aujourd'hui tout en travaillant sur ma réponse à cette question: comment puis-je déterminer quelle fonction était appelé de manière interactive dans la pile?

Voici une fonction qui produit toutes les trames de trace actuelles

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

En utilisant cela dans une macro, nous pouvons rechercher la pile d'expansion pour voir quelle définition de fonction est développée à ce moment-là et mettre cette valeur directement dans le code.

Voici la fonction pour faire l'expansion:

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

Le voici en action.

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

Réponse originale:

Vous pouvez utiliser backtrace-framepour rechercher la pile jusqu'à ce que vous voyiez un cadre qui représente un appel de fonction direct et obtenez le nom de cela.

(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

Ici, je fais la recherche du nom de la fonction lors de l'exécution, mais il est probablement possible de l'implémenter dans une macro qui se développe directement dans le symbole de la fonction, ce qui serait plus performant pour les appels répétés et compilés elisp.

J'ai trouvé ces informations en essayant d'écrire une sorte d'enregistreur d'appels de fonction pour elisp qui peut être trouvé ici sous sa forme incomplète, mais cela pourrait vous être utile. https://github.com/jordonbiondo/call-log

Jordon Biondo
la source
Merci pour le code. Cela résout mon problème et je l'accepterai dans quelques jours à moins que quelqu'un ne nous donne une sorte de variable "nom de la fonction actuelle" qui fait partie du débogueur auquel vous faites allusion.
phs
Soit dit en passant, cela ne semble pas fonctionner quand un defunest entouré par eval-and-compile, c'est-à-dire qu'il revient nil.
Alexander Shukaev