¿Pueden las funciones acceder a su nombre?

25

En C existe la variable mágica __func__que contiene el nombre de la función actual. ¡En Bash, hay una matriz que FUNCNAMEcontiene los nombres de todas las funciones en la pila de llamadas!

¿Hay algo similar en Emacs Lisp? ¿O alguna forma simple de que una función tenga acceso a su nombre?

No he encontrado ninguna respuesta en el manual de Emacs Lisp (capítulo 12 sobre Funciones o índice de variables y funciones y ...)

phs
fuente
2
¿Para qué necesitas eso realmente?
lunaryorn
1
No es exactamente una variable mágica: es una definición previa al procesador insertada por algunos compiladores.
Sean Allred

Respuestas:

6

Para las funciones interactivas, es decir, comandos, puede utilizar la variable this-command, o más seguro, real-this-command. La diferencia es que cuando escribe sus propias funciones, puede cambiar explícitamente el valor de this-command. Principalmente, esto se hace para jugar trucos last-commandrelacionados con comandos repetitivos. No deberías (¿no?) Hacer esto con real-this-command. Siempre será el nombre del comando actual.

No conozco un equivalente para funciones no interactivas.

Tyler
fuente
3
Es importante tener en cuenta eso this-commandy real-last-commandno son para nada similares __func__. Por ejemplo, si el comando A llama al comando B que imprime this-command, imprimirá el comando A, no B, y esto tampoco funciona para las funciones.
Jordon Biondo
1
@JordonBiondo De acuerdo. Noté que no funciona para las funciones. phs no nos dio su contexto, por lo que supuse que esto podría ser lo suficientemente bueno para su aplicación. Su respuesta es más robusta, no hay duda.
Tyler
22

Respuesta actualizada con búsqueda de tiempo de expansión:

Dije en mi respuesta original que puede haber una manera de hacer esto en tiempo de expansión / compilación en lugar de tiempo de ejecución para dar un mejor rendimiento y finalmente lo implementé hoy mientras trabajaba en mi respuesta para esta pregunta: ¿Cómo puedo determinar qué función era llamado interactivamente en la pila?

Aquí hay una función que produce todos los marcos de traza inversa actuales

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

Usando eso en una macro, podemos buscar la pila de expansión para ver qué definición de función se está expandiendo en ese momento y poner ese valor en el código.

Aquí está la función para hacer la expansión:

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

Aquí está en acción.

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

Respuesta original

Puede usar backtrace-framepara buscar la pila hasta que vea el marco que representa una llamada de función directa y obtener el nombre de eso.

(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

Aquí estoy haciendo la búsqueda del nombre de la función en tiempo de ejecución, aunque probablemente sea posible implementar esto en una macro que se expande directamente en el símbolo de la función que sería más eficiente para las llamadas repetidas y elisp compilado.

Encontré esta información al intentar escribir una especie de registrador de llamadas de función para elisp que se puede encontrar aquí en forma incompleta, pero puede ser útil para usted. https://github.com/jordonbiondo/call-log

Jordon Biondo
fuente
Gracias por el codigo. Esto resuelve mi problema y lo aceptaré dentro de unos días, a menos que alguien nos dé algún tipo de variable "nombre-función-actual" que sea parte del depurador que sugiera.
phs
Por cierto, no parece funcionar cuando a defunestá encerrado por eval-and-compile, es decir, regresa nil.
Alexander Shukaev