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.
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))
}
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)
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
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()
}
Related
I'm having difficulty when trying to get path of imported package. When I print result of os.Getwd() inside imported package, it's showing same path like on main package.
This what I did.
Project structure
lib/lib.go
package lib
import "os"
import "fmt"
func init() {
dir, _ := os.Getwd()
fmt.Println("lib.init() :", dir)
}
func GetPath() {
dir, _ := os.Getwd()
fmt.Println("lib.GetPath() :", dir)
}
main.go
package main
import "os"
import "fmt"
import "test-import/lib"
func main() {
dir, _ := os.Getwd()
fmt.Println("main :", dir)
lib.GetPath()
}
Result
lib.init() : /Users/novalagung/Documents/go/src/test-import
main : /Users/novalagung/Documents/go/src/test-import
lib.GetPath() : /Users/novalagung/Documents/go/src/test-import
The result of os.Getwd() from lib/lib.go is still same path like on main. What I want is the real path of the package which is /Users/novalagung/Documents/go/src/test-import/lib/
What should I do? Is it possible?
If you can get a reference to something in the package, you can use reflect to get the import path.
Here's an example on Play:
package main
import (
"bytes"
"fmt"
"reflect"
)
func main() {
var b bytes.Buffer
fmt.Println(reflect.TypeOf(b).PkgPath())
}
PkgPath() only can retrieve the package path for non-pointer
// If the type was predeclared (string, error) or not defined (*T, struct{},
// []int, or A where A is an alias for a non-defined type), the package path
// will be the empty string.
func packageName(v interface{}) string {
if v == nil {
return ""
}
val := reflect.ValueOf(v)
if val.Kind() == reflect.Ptr {
return val.Elem().Type().PkgPath()
}
return val.Type().PkgPath()
}
Problem at reflection, with MethodByName
Code:
package main
import (
"reflect"
"fmt"
)
type test struct {}
var serviceType = map[string]reflect.Value{
"test": reflect.ValueOf(test{}),
}
func (t *test) prnt() {
fmt.Println("test ok")
}
func callFunc(strct string, fName string) {
s := serviceType[strct].MethodByName(fName)
if !s.IsValid(){
fmt.Println("not correct")
return
}
s.Call(make([]reflect.Value,0))
}
func main() {
callFunc("test", "prnt")
}
Output:
not correct
Playground:
https://play.golang.org/p/ZLEQBGYoUOB
Could you help, what I'm doing wrong ?
Two things needs to be corrected.
MethodByName() returns only Exported methods. So you have to rename prnt tp Prnt
Needs to pass a pointer of struct test to reflect.ValueOf() method.
Here is the modified working code https://play.golang.org/p/4MK2kqOz6e2
package main
import (
"fmt"
"bufio"
"os"
)
func main() {
fmt.Print("LOADED!\n")
fmt.Print("insert y value here: ")
inputY := bufio.NewScanner(os.Stdin)
inputY.Scan()
inputXfunc()
}
func inputXfunc() {
fmt.Print("insert x value here: ")
inputX := bufio.NewScanner(os.Stdin)
inputX.Scan()
slope()
}
func slope() {
fmt.Println(inputX.Text())
}
Whenever I run this program, it says, that inputX and inputY are unidentified. How do I make this program use variables that are accessible to all of the functions? All I want to do is devide inputY by inputX then print out the result
I'm just putting my comment as an answer... I would recommend against this however you could just declare the variable at package scope. It would look like this;
package main
import (
"fmt"
"bufio"
"os"
)
var inputX io.Scanner
func main() {
fmt.Print("LOADED!\n")
fmt.Print("insert y value here: ")
inputY := bufio.NewScanner(os.Stdin)
inputY.Scan()
inputXfunc()
}
func inputXfunc() {
fmt.Print("insert x value here: ")
inputX = bufio.NewScanner(os.Stdin) // get rid of assignment initilize short hand here
inputX.Scan()
slope()
}
func slope() {
fmt.Println(inputX.Text())
}
However a better choice would be to change your method definitions to accept arguments and pass the values into them as needed. This would like like so;
func slope(inputX bufio.Scanner) {
fmt.Println(inputX.Text())
}
slope(myInputWhichIsOfTypeIOScanner)
You can create an init() function and make use of it in the main.go by using package like godotenv to set os's environment variables:
global.go file
package global
import (
"log"
"os"
"strconv"
"github.com/joho/godotenv"
)
var (
SERVER_HOST string
SERVER_PORT int
)
func InitConfig() {
err := godotenv.Load()
if err != nil {
log.Fatal("Error loading .env file")
}
SERVER_HOST = os.Getenv("SERVER_HOST")
SERVER_PORT, _ = strconv.Atoi(os.Getenv("SERVER_PORT"))
}
main.go file
package main
import(
G "path/to/config"
)
func init() {
G.InitConfig()
}
func main() {
G.Init()
}
You will still have to import "G" package in other packages to use the variables, but I think the best way to tackle global variables in Go (or any other languages) is to make use of environment variables.
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{})
I'm trying to learn the basics of Go by tweaking examples as I go along the tutorial located here:
http://tour.golang.org/#9
Here's a small function I wrote that just turns ever character to all caps.
package main
import (
"fmt"
"strings"
)
func capitalize(name string) {
name = strings.ToTitle(name)
return
}
func main() {
test := "Sergio"
fmt.Println(capitalize(test))
}
I'm getting this exception:
prog.go:15: capitalize(test) used as value
Any glaring mistakes?
You are missing the return type for capitalize():
package main
import (
"fmt"
"strings"
)
func capitalize(name string) string {
return strings.ToTitle(name)
}
func main() {
test := "Sergio"
fmt.Println(capitalize(test))
}
Playground
Output:
SERGIO