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
Related
I've got function that returns struct instance depending on argument it takes
func Factory(s string) interface{} {
if s == 'SomeType' {
return SomeType{}
} else if s == 'AnotherType' {
return AnotherType{}
}
}
this solution is good if I have a couple of structs to return but it's getting ugly if there are a lot of them, can I do it other way? Is there idiomatic way to do this?
As the comment said, you can use a map for your types. Looks like this. The factory function will return an instance if the type exists or nil if it doesn't.
package main
import (
"fmt"
"reflect"
)
type SomeType struct{ Something string }
type AnotherType struct{}
type YetAnotherType struct{}
var typemap = map[string]interface{}{
"SomeType": SomeType{ Something: "something" },
"AnotherType": AnotherType{},
"YetAnotherType": YetAnotherType{},
}
func factory(s string) interface{} {
t, ok := typemap[s]
if ok {
return reflect.ValueOf(t)
}
return nil
}
func main() {
fmt.Printf("%#v\n", factory("SomeType"))
fmt.Printf("%#v\n", factory("NoType"))
}
Playground link
There is func someFunc(v interface{}, fn interface{}) where v is a pointer to struct (say *A) and fn is a method expression (say (*A).method()). How to call fn with v as parameter (using reflect)?
It's possible to do with reflect.Value.Call(), however it's pretty cumbersome, especially if you need to do something with the return value. But here's a basic example:
package main
import (
"fmt"
"reflect"
)
type Foo struct {
Bar string
}
func concrete(foo *Foo) {
fmt.Printf("Foo: %#v\n", foo)
}
func someFunc(v interface{}, fn interface{}) {
f := reflect.ValueOf(fn)
arg := reflect.ValueOf(v)
f.Call([]reflect.Value{arg})
}
func main() {
foo := Foo{"Bar"}
someFunc(&foo, concrete)
}
// Output: Foo: &main.Foo{Bar:"Bar"}
https://play.golang.org/p/ED6QdvENxti
If you want to call a method of a struct by name, we need to revise it just a bit:
type Foo struct {
Bar string
}
func (f *Foo) Concrete() {
fmt.Printf("Foo: %#v\n", f)
}
func callByName(v interface{}, fn string) {
arg := reflect.ValueOf(v)
f := arg.MethodByName(fn)
f.Call([]reflect.Value{})
}
func main() {
foo := Foo{"Bar"}
callByName(&foo, "Concrete")
}
Notice that in this case the method value is already bound to the struct instance, so we don't need to pass it anything as an argument.
So to the challenge of using reflect to call a method without knowing name and being a method of the same type, as op (orignally) addressed, I have come up with a very, very ugly and unsafe way.
package main
import (
"fmt"
"reflect"
"runtime"
"strings"
)
func getFName(v reflect.Value) string {
return runtime.FuncForPC(v.Pointer()).Name()
}
func callMethod(a,fn interface{}) {
fname:=getFName(reflect.ValueOf(fn))
parts:=strings.Split(fname,".")
name:=strings.TrimSuffix(parts[len(parts)-1],"-fm")
reflect.ValueOf(a).MethodByName(name).Call(nil)
}
func (s *a) Tst() {
fmt.Println(s)
}
type a struct { p string }
func main() {
x,y:=a{"Good"},a{"Bad"}
callMethod(&x,y.Tst)
}
Playground: https://play.golang.org/p/QgrGhI5DC3p
I would like to know a better way that does not rely on how runtime.FuncForPc formats methods (which is not version-safe), but this is the best I have. Please do tell me any ideas to improve it.
Consider the following trivial main.go file:
package main
type myStruct struct {
songs []string
}
func main() {
s1 := myStruct{
songs:[]string{"Master of Puppets", "Battery"}
}
foo(s1)
}
func foo(s myStruct) {
// Does something with s
}
I get how I could test the foo function. But how could I test that the main function properly intializes the s1 struct and assert that the foo function get's called with s1 as an argument?
Why not try our most basic debug tool: printf.
package main
import "fmt"
type myStruct struct {
songs []string
}
func main() {
s1 := myStruct{songs:[]string{"Master of Puppets", "Battery"}}
foo(s1)
}
func foo(s myStruct) {
// Does something with s
for i:=0;i<len(s.songs);i++ {
fmt.Printf("s.songs[%d]=\"%s\"\n",i, s.songs[i])
}
}
output:
s.songs[0]="Master of Puppets"
s.songs[1]="Battery"
In case you really want to test if your structure was properly initialized inside your main function:
package main
import "fmt"
type myStruct struct {
songs []string
}
func main() {
s1 := myStruct{songs:[]string{"Master of Puppets", "Battery"}}
foo(s1)
for i:=0;i<len(s1.songs);i++ {
fmt.Printf("s1.songs[%d]=\"%s\"\n",i, s1.songs[i])
}
}
func foo(s myStruct) {
// Does something with s
fmt.Printf("foo has finished its work!\n")
}
output:
foo has finished its work!
s1.songs[0]="Master of Puppets"
s1.songs[1]="Battery"
If you want to make sure both main() and foo() are working with the same object, try this:
package main
import "fmt"
type myStruct struct {
songs []string
}
func main() {
s1 := myStruct{songs:[]string{"Master of Puppets", "Battery"}}
foo(&s1)
fmt.Printf("main() says: s1 lives in the address:%p\n",&s1)
}
func foo(s *myStruct) {
// Does something with s
fmt.Printf("foo() says: s lives in the address:%p\n",s)
}
output:
foo() says: s lives in the address:0x10434120
main() says: s1 lives in the address:0x10434120
Next time, try being more specific on your questions.
Cheers.
I am studying GO by using "a tour of go"
The code is doing very simple things, combine first and last together and output on screen.
The output is a hex address instead of "aaabbb" after I run the code. Could anyone can help me out? Thank you
package main
import "fmt"
type Name struct{
first,last string
}
func (name Name) fullName() string{
return (name.first + name.last)
}
func main(){
v := Name{"aaa","bbb"}
fmt.Println(v.fullName)
}
You are not calling the function fullName. you are just passing 'the pointer' to it:
see this http://play.golang.org/p/GjibbfoyH0
package main
import "fmt"
type Name struct {
first, last string
}
func (name Name) fullName() string {
return (name.first + name.last)
}
func main() {
v := Name{"aaa", "bbb"}
fmt.Println(v.fullName())
}
Use the result of the method
fmt.Println(v.fullName())
not the address of the method
fmt.Println(v.fullName)
For example,
package main
import "fmt"
type Name struct{
first,last string
}
func (name Name) fullName() string{
return (name.first + name.last)
}
func main(){
v := Name{"aaa","bbb"}
fmt.Println(v.fullName())
}
Output:
aaabbb
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()
}