Get name of function using reflection
Asked Answered
P

4

28

I'm trying to use Go's reflection system to retrieve the name of a function but I get an empty string when calling the Name method on its type. Is this the expected behavior?

This is a simple example of how I approach the problem:

package main

import "fmt"
import "reflect"

func main() {
    typ := reflect.TypeOf(main)
    name := typ.Name()
    fmt.Println("Name of function" + name)
}
Partee answered 24/5, 2012 at 17:51 Comment(3)
It seems to me that the type of main is function. What would you expect as a name ?Forlorn
That's a very point. The code example should probably not work, but I think the name of the question is valid.Partee
Possible duplicate of How to get the name of a function in Go?Centner
A
40

The solution is to use FuncForPc which returns a *Func.

This returns "main.main" :

package main

import "fmt"
import "reflect"
import "runtime"


func main() {
    name := runtime.FuncForPC(reflect.ValueOf(main).Pointer()).Name()
    fmt.Println("Name of function : " + name)
}

If you want "main", just tokenize it.

Armoured answered 24/5, 2012 at 18:4 Comment(2)
Or, without reflect, pc, _, _, _ := runtime.Caller(0) fmt.Println("Name of function: " + runtime.FuncForPC(pc).Name())Stanfill
Interesting. But I was assuming OP was looking for a more general solution, with any function.Forlorn
S
33
package main

import "fmt"
import "runtime"

func main() {
    pc, _, _, _ := runtime.Caller(0)
    fmt.Println("Name of function: " + runtime.FuncForPC(pc).Name())
    fmt.Println()

    // or, define a function for it
    fmt.Println("Name of function: " + funcName())
    x()
}

func funcName() string {
    pc, _, _, _ := runtime.Caller(1)
    return runtime.FuncForPC(pc).Name()
}

func x() {
    fmt.Println("Name of function: " + funcName())
    y()
}

func y() {
    fmt.Println("Name of function: " + funcName())
    z()
}
func z() {
    fmt.Println("Name of function: " + funcName())
}

Output:

Name of function: main.main

Name of function: main.main
Name of function: main.x
Name of function: main.y
Name of function: main.z

Stanfill answered 24/5, 2012 at 19:8 Comment(2)
Personally, I prefer your solution (or @Koala3's extension on your own solution), mostly because it allows to encapsulate the runtime calls inside a function which can then be 'safely' deployed elsewhere; if the Go runtime functions for some reason change, then the whole code needs to be changed at just one spot. Also, avoiding reflection is a neat trick, well done! :)Heidy
Great answer Sonia! One caution is that FuncForPC(pc) returns a pointer, so you should check for nil always. I will share as an answer a full production-level utility function based on the core principles above. Thanks!Gnarly
D
4
import runtime

func funcName() string {
    pc, _, _, _ := runtime.Caller(1)
    nameFull := runtime.FuncForPC(pc).Name()    // main.foo
    nameEnd := filepath.Ext(nameFull)           // .foo
    name := strings.TrimPrefix(nameEnd, ".")    // foo
    return name
}
Ducal answered 16/1, 2017 at 9:0 Comment(1)
The advantage of keeping the package name with the function name is, of course, that it's possible to have several functions with the same name in different packages... and then you have no idea which is which :) In fact, the main reason for me to search for this answer on SO was because I was unsure which of my functions was causing an error, and I needed to track down the precise package name besides the function name... Note that you need to import stringsand filepathas well for your solution to work!Heidy
G
0

This is a tested production ready utility function for returning function name.

Note 1: We are handling the possibility of a nil pointer from FuncForPC

Note 2: optFuncLevel is just a friendly name for stack frame level. This gives us the flexibility of using this within another layer of utility functions. A direct call from say main would just pass 1 (or nothing since default), but if I am calling FunctionName in a log enriching function, say PrettyLog() that is called from regular code, I would call it as FunctionName(2) in the call from PrettyLog, so the function name returned is the name of the caller of PrettyLog, not PrettyLog itself.

// FunctionName returns the function name of the caller
// optFuncLevel passes the function level to go back up.
// The default is 1, referring to the caller of this function
func FunctionName(optFuncLevel ...int) (funcName string) {
    frameLevel := 1 // default to the caller's frame
    if len(optFuncLevel) > 0 {
        frameLevel = optFuncLevel[0]
    }

    if pc, _, _, ok := runtime.Caller(frameLevel); ok {
        fPtr := runtime.FuncForPC(pc)
        if fPtr == nil {
            return
        }
        // Shorten full function name a bit
        farr := strings.SplitN(fPtr.Name(), "/", 2)
        if len(farr) < 2 {
            return
        }
        return farr[1]
    }

    return
}
Gnarly answered 22/10, 2021 at 16:24 Comment(0)

© 2022 - 2024 — McMap. All rights reserved.