Como obter o nome de uma função no Go?

101

Dada uma função, é possível obter seu nome? Dizer:

func foo() {
}

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

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

Disseram-me que o runtime.FuncForPC ajudaria, mas não consegui entender como usá-lo.

moraes
fonte

Respostas:

188

Desculpe por responder minha própria pergunta, mas encontrei uma solução:

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
fonte
2
Embora isso pareça funcionar, alguns cuidados podem ser necessários aqui: a documentação para .Pointer () declara "Se v's Kind for Func, o ponteiro retornado é um ponteiro de código subjacente, mas não necessariamente o suficiente para identificar uma única função exclusivamente. O único garantia é que o resultado será zero se e somente se v for um valor nil func. "
jochen
1
@jochen "não é uma única função" significa que ela pode retornar falsos positivos (ou seja, o ponteiro de uma função diferente)?
themihai
1
@themihai Não sei, a frase que citei são todos os documentos em golang.org/pkg/reflect/#Value.Pointer dizem sobre isso. Mas a citação parece indicar que se pode obter o mesmo ponteiro para funções diferentes, não é? E se for esse o caso, GetFunctionNamepode retornar o mesmo nome para funções diferentes?
jochen
3
@jochen acho que isso tem a ver com fechamentos; se você criar dois fechamentos com a mesma função subjacente, eles serão equivalentes, mesmo se os valores sobre os quais eles fecham forem diferentes.
Alex Guerra
9

Não é exatamente o que você quer, porque ele registra o nome do arquivo e o número da linha, mas aqui está como eu faço isso na minha biblioteca Tideland Common Go ( http://tideland-cgl.googlecode.com/ ) usando o pacote "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)
tema
fonte
1
Isso realmente não ajuda. Não preciso obter informações sobre a pilha de chamadas, mas sobre uma determinada função. Acredito que a pergunta seria respondida se eu soubesse como obter o pc correspondente com uma referência de função (se possível).
moraes