Wie erhalte ich den Namen einer Funktion in Go?

101

Ist es bei einer gegebenen Funktion möglich, ihren Namen zu erhalten? Sagen:

func foo() {
}

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

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

Mir wurde gesagt, dass runtime.FuncForPC helfen würde, aber ich verstand nicht, wie man es benutzt.

Moraes
quelle

Antworten:

187

Entschuldigung für die Beantwortung meiner eigenen Frage, aber ich habe eine Lösung gefunden:

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
quelle
2
Während dies zu funktionieren scheint, kann hier einige Sorgfalt erforderlich sein: In der Dokumentation zu .Pointer () heißt es: "Wenn die Art von v Func ist, ist der zurückgegebene Zeiger ein zugrunde liegender Codezeiger, aber nicht unbedingt genug, um eine einzelne Funktion eindeutig zu identifizieren. Die einzige Garantie ist, dass das Ergebnis genau dann Null ist, wenn v ein Null-Funktionswert ist. "
Jochen
1
@jochen bedeutet "keine einzige Funktion", dass falsch positive Ergebnisse zurückgegeben werden können (dh der Zeiger einer anderen Funktion)?
Themihai
1
@themihai Ich weiß nicht, der Satz, den ich zitiert habe, enthält alle Dokumente unter golang.org/pkg/reflect/#Value.Pointer . Aber das Zitat scheint darauf hinzudeuten, dass man den gleichen Zeiger für verschiedene Funktionen bekommen könnte, nicht wahr? Und wenn dies der Fall ist, GetFunctionNamekönnte der gleiche Name für verschiedene Funktionen zurückgegeben werden?
Jochen
3
@jochen Ich denke, das hat mit Schließungen zu tun; Wenn Sie zwei Abschlüsse erstellen, die dieselbe zugrunde liegende Funktion haben, sind sie auch dann gleich, wenn die Werte, über die sie schließen, unterschiedlich sind.
Alex Guerra
9

Nicht genau das, was Sie wollen, da es den Dateinamen und die Zeilennummer protokolliert, aber hier ist, wie ich es in meiner Tideland Common Go-Bibliothek ( http://tideland-cgl.googlecode.com/ ) mit dem "Laufzeit" -Paket mache :

// 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)
Thema
quelle
1
Das hilft nicht wirklich. Ich brauche keine Informationen über den Aufrufstapel, sondern über eine bestimmte Funktion. Ich glaube, die Frage wäre beantwortet, wenn ich wüsste, wie man den entsprechenden PC mit einer Funktionsreferenz erhält (wenn es möglich ist).
Moraes