Bagaimana cara mendapatkan nama suatu fungsi di Go?

101

Diberikan sebuah fungsi, apakah mungkin untuk mendapatkan namanya? Mengatakan:

func foo() {
}

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

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

Saya diberi tahu bahwa runtime.FuncForPC akan membantu, tetapi saya gagal memahami cara menggunakannya.

moraes
sumber

Jawaban:

188

Maaf telah menjawab pertanyaan saya sendiri, tetapi saya menemukan solusi:

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
sumber
2
Meskipun ini tampaknya berhasil, beberapa kehati-hatian mungkin diperlukan di sini: dokumentasi untuk .Pointer () menyatakan "Jika Jenis v adalah Func, penunjuk yang dikembalikan adalah penunjuk kode yang mendasari, tetapi belum tentu cukup untuk mengidentifikasi satu fungsi secara unik. Satu-satunya jaminan adalah bahwa hasilnya nol jika dan hanya jika v adalah nilai fungsi nihil. "
jochen
1
@ Jochen tidak "tidak satu fungsi" berarti bahwa ia dapat mengembalikan positif palsu (yaitu penunjuk dari fungsi yang berbeda)?
themihai
1
@themihai Saya tidak tahu, kalimat yang saya kutip adalah semua dokumen di golang.org/pkg/reflect/#Value.Pointer katakan tentang ini. Tetapi kutipan tersebut tampaknya menunjukkan bahwa seseorang bisa mendapatkan penunjuk yang sama untuk fungsi yang berbeda, bukan? Dan jika ini masalahnya, GetFunctionNamemungkinkah mengembalikan nama yang sama untuk fungsi yang berbeda?
jochen
3
@ Jochen Saya pikir ini ada hubungannya dengan penutupan; jika Anda membuat dua closure yang memiliki fungsi dasar yang sama, keduanya akan setara meskipun nilai yang ditutupnya berbeda.
Alex Guerra
9

Tidak persis seperti yang Anda inginkan, karena ini mencatat nama file dan nomor baris, tetapi inilah cara saya melakukannya di Perpustakaan Tideland Common Go saya ( http://tideland-cgl.googlecode.com/ ) menggunakan paket "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)
themue
sumber
1
Itu tidak terlalu membantu. Saya tidak perlu mendapatkan informasi tentang tumpukan panggilan, tetapi tentang fungsi yang diberikan. Saya yakin pertanyaan itu akan terjawab jika saya tahu bagaimana mendapatkan pc yang sesuai dengan referensi fungsi (jika memungkinkan).
moraes