Comment obtenir le nom d'une fonction dans Go?

101

Étant donné une fonction, est-il possible d'obtenir son nom? Dire:

func foo() {
}

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

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

On m'a dit que runtime.FuncForPC aiderait, mais je n'ai pas compris comment l'utiliser.

moraes
la source

Réponses:

188

Désolé d'avoir répondu à ma propre question, mais j'ai trouvé une solution:

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
la source
2
Bien que cela semble fonctionner, quelques précautions peuvent être nécessaires ici: la documentation de .Pointer () indique «Si v's Kind est Func, le pointeur renvoyé est un pointeur de code sous-jacent, mais pas nécessairement suffisant pour identifier une seule fonction de manière unique. Le seul la garantie est que le résultat est zéro si et seulement si v est une valeur func nulle. "
jochen
1
@jochen "pas une seule fonction" signifie-t-il qu'il pourrait renvoyer des faux positifs (c'est-à-dire le pointeur d'une fonction différente)?
themihai
1
@themihai Je ne sais pas, la phrase que j'ai citée est toute la documentation sur golang.org/pkg/reflect/#Value.Pointer dit à ce sujet. Mais la citation semble indiquer que l'on pourrait obtenir le même pointeur pour différentes fonctions, n'est-ce pas? Et si tel est le cas, GetFunctionNamepeut renvoyer le même nom pour différentes fonctions?
jochen
3
@jochen Je pense que cela a à voir avec les fermetures; si vous créez deux fermetures qui ont la même fonction sous-jacente, elles seront équivalentes même si les valeurs sur lesquelles elles se ferment sont différentes.
Alex Guerra
9

Pas exactement ce que vous voulez, car il enregistre le nom de fichier et le numéro de ligne, mais voici comment je le fais dans ma bibliothèque Tideland Common Go ( http://tideland-cgl.googlecode.com/ ) en utilisant le package "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)
thème
la source
1
Cela n'aide pas vraiment. Je n'ai pas besoin d'obtenir des informations sur la pile d'appels, mais sur une fonction donnée. Je crois que la question serait répondue si je savais comment obtenir le pc correspondant avec une référence de fonction (si c'est possible).
moraes