Jak uzyskać nazwę funkcji w Go?

101

Czy w przypadku danej funkcji można uzyskać jej nazwę? Mówić:

func foo() {
}

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

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

Powiedziano mi, że runtime.FuncForPC może pomóc, ale nie rozumiałem, jak go używać.

moraes
źródło

Odpowiedzi:

189

Przepraszam, że odpowiadam na moje własne pytanie, ale znalazłem rozwiązanie:

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
źródło
2
Chociaż wydaje się, że to działa, w tym miejscu może być wymagana ostrożność: dokumentacja dla .Pointer () stwierdza: „Jeśli typ v to Func, zwrócony wskaźnik jest podstawowym wskaźnikiem kodu, ale niekoniecznie wystarczającym do jednoznacznej identyfikacji pojedynczej funkcji. Jedyny gwarancją jest to, że wynikiem jest zero wtedy i tylko wtedy, gdy v jest zerową wartością funkcji. "
jochen
1
@jochen czy „not a func” oznacza, że ​​może zwracać fałszywe alarmy (tj. wskaźnik innej funkcji)?
themihai
1
@themihai Nie wiem, zdanie, które zacytowałem, to wszystkie dokumenty na golang.org/pkg/reflect/#Value.Pointer mówią o tym. Ale cytat wydaje się wskazywać, że można uzyskać ten sam wskaźnik dla różnych funkcji, prawda? A jeśli tak jest, czy GetFunctionNamemoże zwrócić tę samą nazwę dla różnych funkcji?
jochen
3
@jochen Myślę, że ma to związek z zamknięciami; jeśli utworzysz dwa zamknięcia, które mają tę samą podstawową funkcję, będą one równoważne, nawet jeśli wartości, które zamykają, są różne.
Alex Guerra,
9

Nie jest to dokładnie to, czego chcesz, ponieważ rejestruje nazwę pliku i numer linii, ale oto jak to robię w mojej bibliotece Tideland Common Go ( http://tideland-cgl.googlecode.com/ ) przy użyciu pakietu „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)
motyw
źródło
1
To naprawdę nie pomaga. Nie potrzebuję informacji o stosie wywołań, ale o danej funkcji. Uważam, że odpowiedź na to pytanie byłaby, gdybym wiedział, jak uzyskać odpowiedni komputer z podaniem odwołania do funkcji (jeśli jest to możliwe).
moraes