¿Cómo obtener el nombre de una función en Go?

101

Dada una función, ¿es posible obtener su nombre? Decir:

func foo() {
}

func GetFunctionName(i interface{}) string {
    // ...
}

func main() {
    // Will print "name: foo"
    fmt.Println("name:", GetFunctionName(foo))
}

Me dijeron que runtime.FuncForPC ayudaría, pero no entendí cómo usarlo.

moraes
fuente

Respuestas:

187

Perdón por responder mi propia pregunta, pero encontré una solución:

package main

import (
    "fmt"
    "reflect"
    "runtime"
)

func foo() {
}

func GetFunctionName(i interface{}) string {
    return runtime.FuncForPC(reflect.ValueOf(i).Pointer()).Name()
}

func main() {
    // This will print "name: main.foo"
    fmt.Println("name:", GetFunctionName(foo))
}
moraes
fuente
2
Si bien esto parece funcionar, es posible que se requiera algo de cuidado aquí: la documentación para .Pointer () dice "Si v's Kind es Func, el puntero devuelto es un puntero de código subyacente, pero no necesariamente lo suficiente como para identificar una única función de forma única. la garantía es que el resultado es cero si y solo si v es un valor de función nulo ".
jochen
1
@jochen ¿"ni una sola función" significa que podría devolver falsos positivos (es decir, el puntero de una función diferente)?
themihai
1
@themihai No lo sé, la oración que cité son todos los documentos en golang.org/pkg/reflect/#Value.Pointer que dicen sobre esto. Pero la cita parece indicar que uno podría obtener el mismo puntero para diferentes funciones, ¿no es así? Y si este es el caso, ¿ GetFunctionNamepodría devolver el mismo nombre para diferentes funciones?
Jochen
3
@jochen Creo que esto tiene que ver con los cierres; si crea dos cierres que tienen la misma función subyacente, serán equivalentes incluso si los valores sobre los que cierran son diferentes.
Alex Guerra
9

No es exactamente lo que desea, porque registra el nombre del archivo y el número de línea, pero así es como lo hago en mi biblioteca Tideland Common Go ( http://tideland-cgl.googlecode.com/ ) usando el paquete "runtime":

// Debug prints a debug information to the log with file and line.
func Debug(format string, a ...interface{}) {
    _, file, line, _ := runtime.Caller(1)
    info := fmt.Sprintf(format, a...)

    log.Printf("[cgl] debug %s:%d %v", file, line, info)
ellos
fuente
1
Eso realmente no ayuda. No necesito obtener información sobre la pila de llamadas, sino sobre una función determinada. Creo que la pregunta se respondería si supiera cómo obtener la PC correspondiente dada una referencia de función (si es posible).
moraes