How to get the name of a function in Go?
Asked Answered
S

4

145

Given a function, is it possible to get its name? Say:

func foo() {
}

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

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

I was told that runtime.FuncForPC would help, but I failed to understand how to use it.

Subgenus answered 13/8, 2011 at 19:12 Comment(0)
S
267

I found a 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))
}
Subgenus answered 13/8, 2011 at 23:10 Comment(6)
While this seems to work, some care may be required here: the documentation for .Pointer() states "If v's Kind is Func, the returned pointer is an underlying code pointer, but not necessarily enough to identify a single function uniquely. The only guarantee is that the result is zero if and only if v is a nil func Value."Cata
@Cata does "not a single func" means that it could return false positives (i.e. the pointer of a different function) ?Laws
@Laws I don't know, the sentence I quoted is all the docs at golang.org/pkg/reflect/#Value.Pointer say about this. But the quote seems to indicate that one could get the same pointer for different functions, doesn't it? And if this is the case, GetFunctionName might return the same name for different functions?Cata
@Cata I think this has to do with closures; if you create two closures that have the same underlying function, they will be equivalent even if the values they close over are different.Rhearheba
There are some issues with this approach when used on methods on generic structs: github.com/golang/go/issues/63945Reclamation
Optimization could cause multiple functions to have the same address. Consider a number of functions or methods that do nothing and return nothing, for example: they could be code-space optimized to a single function. Having such functions isn't unusual during ramp-up.Arbitral
M
14

Not exactly what you want, because it logs the filename and the line number, but here is how I do it in my Tideland Common Go Library (http://tideland-cgl.googlecode.com/) using the "runtime" package:

// 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)
Miscarry answered 13/8, 2011 at 21:39 Comment(1)
That doesn't really help. I don't need to get information about the call stack, but about a given function. I believe the question would be answered if I knew how to get the corresponding pc given a function reference (if it is possible).Subgenus
Z
7

I found a better solution, in this function down here you just simply pass a function and the output is gonna be simple and straight.

package main

import (
    "reflect"
    "runtime"
    "strings"
)

func GetFunctionName(temp interface{}) string {
    strs := strings.Split((runtime.FuncForPC(reflect.ValueOf(temp).Pointer()).Name()), ".")
    return strs[len(strs)-1]
}

And this is an example of how you use this:

package main

import "fmt"

func main() {
    fmt.Println(GetFunctionName(main))
}

And this is the answer you should expect:

main
Zacek answered 30/12, 2021 at 19:18 Comment(4)
What's the point of this? You're literally just returning the same string you passed to it. May as well just do fmt.Println("main")Caresa
@Caresa I passed a function, not a string... This solution is actually what the question wanted. Read the question again.Zacek
you're right, the question does ask that, I apologise. I think I read between the lines a bit on the question because it made no sense to me as written. It seems completely pointless to get the name of a function by passing the function's name. If one already has the name, just print it or use it, why pass that name to another function which simply returns the same name back.Caresa
The point, at least why I'm searching for the answer, is that you could write a library that expects users to pass in a function and for feedback use the name of the function to do something. For instance, a test framework that would use the name for identifying the test name.Orthopedics
D
5

By getting the function name of the previous caller:

import (
    "os"
    "runtime"
)

func currentFunction() string {
    counter, _, _, success := runtime.Caller(1)

    if !success {
        println("functionName: runtime.Caller: failed")
        os.Exit(1)
    }

    return runtime.FuncForPC(counter).Name()
}
Damp answered 13/10, 2021 at 3:18 Comment(0)

© 2022 - 2024 — McMap. All rights reserved.