Is it possible get information about caller function in Golang? For example if I have
func foo() {
//Do something
}
func main() {
foo()
}
How can I get that foo has been called from main?
I'm able to this in other language (for example in C# I just need to use CallerMemberName class attribute)
You can use runtime.Caller for easily retrieving information about the caller:
func Caller(skip int) (pc uintptr, file string, line int, ok bool)
Example #1: Print caller file name and line number: https://play.golang.org/p/cdO4Z4ApHS
package main
import (
"fmt"
"runtime"
)
func foo() {
_, file, no, ok := runtime.Caller(1)
if ok {
fmt.Printf("called from %s#%d\n", file, no)
}
}
func main() {
foo()
}
Example #2: Get more information with runtime.FuncForPC: https://play.golang.org/p/y8mpQq2mAv
package main
import (
"fmt"
"runtime"
)
func foo() {
pc, _, _, ok := runtime.Caller(1)
details := runtime.FuncForPC(pc)
if ok && details != nil {
fmt.Printf("called from %s\n", details.Name())
}
}
func main() {
foo()
}
expanding on my comment, here's some code that returns the current func's caller
import(
"fmt"
"runtime"
)
func getFrame(skipFrames int) runtime.Frame {
// We need the frame at index skipFrames+2, since we never want runtime.Callers and getFrame
targetFrameIndex := skipFrames + 2
// Set size to targetFrameIndex+2 to ensure we have room for one more caller than we need
programCounters := make([]uintptr, targetFrameIndex+2)
n := runtime.Callers(0, programCounters)
frame := runtime.Frame{Function: "unknown"}
if n > 0 {
frames := runtime.CallersFrames(programCounters[:n])
for more, frameIndex := true, 0; more && frameIndex <= targetFrameIndex; frameIndex++ {
var frameCandidate runtime.Frame
frameCandidate, more = frames.Next()
if frameIndex == targetFrameIndex {
frame = frameCandidate
}
}
}
return frame
}
// MyCaller returns the caller of the function that called it :)
func MyCaller() string {
// Skip GetCallerFunctionName and the function to get the caller of
return getFrame(2).Function
}
// foo calls MyCaller
func foo() {
fmt.Println(MyCaller())
}
// bar is what we want to see in the output - it is our "caller"
func bar() {
foo()
}
func main(){
bar()
}
For more examples: https://play.golang.org/p/cv-SpkvexuM
Related
Using Go v1.14.3, I'm trying to do the following:
package main
import (
"os"
"github.com/mihaigalos/go-bar/bar"
)
var progressBar bar.Bar
func (*ProgressHandler) New(begin int, end int) {
progressBar.New(begin, end)
}
func main() {
var progressHandler ProgressHandler
send(&progressHandler)
}
So far so good. But when I test, I have no need to see any progressbar, hence my ProgressHandler can be empty, and I want to dependency-inject this object into send().
Inside it will call my specified New().
package main
import (
"testing"
)
func (*ProgressHandler) New(int, int) {
}
func TestSendWorks_whenTypical(t *testing.T) {
expected := true
var progressHandler ProgressHandler
actual := send(&progressHandler)
if actual != expected {
t.Errorf("No Match: %b != %b", actual, expected)
}
}
When I compile this, I get a name collision with the handlers defined in main:
integration_typical_test.go:23:25: (*ProgressHandler).New redeclared in this block
previous declaration at main.go:15:6
I've tried changing the name of the package to something different. The same error there.
How can I improve here?
Use an interface.
type ProgressBar interface {
New(int, int)
}
var progressBar bar.Bar
// New implements ProgressBar
func (*ProgressHandler) New(begin int, end int) {
progressBar.New(begin, end)
}
// change *ProgressHandler to ProgressBar
func send(pb ProgressBar) {
// ...
}
func main() {
var progressHandler ProgressHandler
send(&progressHandler)
}
package main
import (
"testing"
)
type fakeProgressHandler struct{}
func (*fakeProgressHandler) New(int, int) {
}
func TestSendWorks_whenTypical(t *testing.T) {
expected := true
var progressHandler fakeProgressHandler
actual := send(&progressHandler)
if actual != expected {
t.Errorf("No Match: %b != %b", actual, expected)
}
}
Handle nil receiver in the handler.
package main
import (
"os"
"github.com/mihaigalos/go-bar/bar"
)
var progressBar bar.Bar
func (h *ProgressHandler) New(begin int, end int) {
if h == nil {
return
}
progressBar.New(begin, end)
}
func main() {
var progressHandler ProgressHandler
send(&progressHandler)
}
package main
import (
"testing"
)
func TestSendWorks_whenTypical(t *testing.T) {
expected := true
actual := send(nil)
if actual != expected {
t.Errorf("No Match: %b != %b", actual, expected)
}
}
I'm not quite sure how to address this question, please feel free to edit.
With the first code block below, I am able to check if a all fields of a struct are nil.
In reality however, the values injected in the struct, are received as args.Review (see second code block below).
In the second code block, how can I check if all fields from args.Review are nil?
Try it on Golang Playground
package main
import (
"fmt"
"reflect"
)
type review struct {
Stars *int32 `json:"stars" bson:"stars,omitempty" `
Commentary *string `json:"commentary" bson:"commentary,omitempty"`
}
func main() {
newReview := &review{
Stars: nil,
// Stars: func(i int32) *int32 { return &i }(5),
Commentary: nil,
// Commentary: func(i string) *string { return &i }("Great"),
}
if reflect.DeepEqual(review{}, *newReview) {
fmt.Println("Nothing")
} else {
fmt.Println("Hello")
}
}
Try the second code on Golang Playground
This code below gets two errors:
prog.go:32:14: type args is not an expression
prog.go:44:27: args.Review is not a type
package main
import (
"fmt"
"reflect"
)
type review struct {
Stars *int32 `json:"stars" bson:"stars,omitempty" `
Commentary *string `json:"commentary" bson:"commentary,omitempty"`
}
type reviewInput struct {
Stars *int32
Commentary *string
}
type args struct {
PostSlug string
Review *reviewInput
}
func main() {
f := &args {
PostSlug: "second-post",
Review: &reviewInput{
Stars: func(i int32) *int32 { return &i }(5),
Commentary: func(i string) *string { return &i }("Great"),
},
}
createReview(args)
}
func createReview(args *struct {
PostSlug string
Review *reviewInput
}) {
g := &review{
Stars: args.Review.Stars,
Commentary: args.Review.Commentary,
}
if reflect.DeepEqual(args.Review{}, nil) {
fmt.Println("Nothing")
} else {
fmt.Println("Something")
}
}
If you're dealing with a small number of fields you should use simple if statements to determine whether they are nil or not.
if args.Stars == nil && args.Commentary == nil {
// ...
}
If you're dealing with more fields than you would like to manually spell out in if statements you could use a simple helper function that takes a variadic number of interface{} arguments. Just keep in mind that there is this: Check for nil and nil interface in Go
func AllNil(vv ...interface{}) bool {
for _, v := range vv {
if v == nil {
continue
}
if rv := reflect.ValueOf(v); !rv.IsNil() {
return false
}
}
return true
}
if AllNil(args.Stars, args.Commentary, args.Foo, args.Bar, args.Baz) {
// ...
}
Or you can use the reflect package to do your bidding.
func NilFields(x interface{}) bool {
rv := reflect.ValueOf(args)
rv = rv.Elem()
for i := 0; i < rv.NumField(); i++ {
if f := rv.Field(i); f.IsValid() && !f.IsNil() {
return false
}
}
return true
}
if NilFields(args) {
// ...
}
Is it possible in GO to find structs or functions by criteria such as name, tag or interface? i.e something along the lines of command line tasks/verbs? i.e:
func cmd1() {
...
}
func cmd2() {
...
}
...
func cmdN() {
}
func main() {
// Inspect os.Args and call cmd{X}() based on args.
...
}
I don't mind what the exact mechanism is and if the final targets are functions or structs - the goal is to get something working by convention without any boilerplate code.
You could use reflection
package main
import (
"flag"
"fmt"
"reflect"
)
var cmd command
type command struct{}
func (c command) execute(name string) {
v := reflect.ValueOf(c)
cmd := v.MethodByName(name)
if !cmd.IsValid() {
fmt.Println(name + " not a command")
return
}
cmd.Call([]reflect.Value{})
}
func (c command) Cmd1() {
fmt.Println("command 1")
}
func (c command) Cmd2() {
fmt.Println("command 2")
}
func (c command) Cmd3() {
fmt.Println("command 3")
}
func main() {
flag.Parse()
cmd.execute(flag.Arg(0))
}
or you could use a map.
package main
import (
"flag"
"fmt"
)
func cmd1() {
fmt.Println("command 1")
}
func cmd2() {
fmt.Println("command 2")
}
func cmd3() {
fmt.Println("command 3")
}
var funcs = map[string]func(){
"cmd1": cmd1,
"cmd2": cmd2,
"cmd3": cmd3,
}
func main() {
flag.Parse()
if f, ok := funcs[flag.Arg(0)]; ok {
f()
} else {
fmt.Println(flag.Arg(0) + " command not found")
}
}
I used a similar approach in "How to test a collection of functions by reflection in Go?"
The idea is to list and find all the functions needed, in my case, functions for a certain struct type:
stype := reflect.ValueOf(s)
for _, fname := range funcNames {
sfunc := stype.MethodByName(fname)
// no parameter => empty slice of Value
ret := sfunc.Call([]reflect.Value{})
Can I set function pointer to function with receiver simpler than creating function around it?
package main
import "fmt"
type hello struct {
name string
}
func (obj *hello) hello() {
fmt.Printf("Hello %s\n", obj.name)
}
func ntimes(action func (), n int) {
for i := 0; i < n; i++ {
action()
}
}
func main() {
obj := hello{"world"}
// Can I do following simpler?
ntimes(func() {obj.hello();}, 3)
}
Not right now. But with Go 1.1 this will be possible. Go 1.1 Function Calls
Go 1.1 will be ready when the blue line touches zero.
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)
}
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.
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
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
}
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
}