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.
Related
I have tried the following to update an empty map declared as a struct field:
package main
type MyStruct struct {
scoreboard map[string]int
}
func main() {
mystruct := NewMyStruct()
mystruct.SubmitWord('test')
}
func NewMyStruct() MyStruct {
return MyStruct{}
}
func (mystruct *MyStruct) SubmitWord(word string) int {
mystruct.scoreboard[word] = len(word)
return len(word)
}
but I get an error with exit status 2.
The problematic line being mystruct.scoreboard[word] = len(word)
Anything I can find seems to suggest this is ok but I haven't found any other examples where the map is within a struct.
you need to allocate the map first
package main
type MyStruct struct {
scoreboard map[string]int
}
func main() {
mystruct := NewMyStruct()
mystruct.SubmitWord("test")
}
func NewMyStruct() MyStruct {
var x MyStruct
x.scoreboard=make(map[string]int)
return x
}
func (mystruct *MyStruct) SubmitWord(word string) int {
mystruct.scoreboard[word] = len(word)
return len(word)
}
playground : https://play.golang.org/p/ipqHJ8TdUfh
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
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.
I want to use some external code that requires a pointer to a struct. At the point that the code is called, I have an interface variable.
When creating a pointer off of that variable, the pointer's type is interface{}* when I need it to be the pointer type of the struct's type.
Image the code in TestCanGetStructPointer does not know about the Cat class, and that it exists in some external package.
How can I cast it to this?
Here is a code sample:
import (
"reflect"
"testing"
)
type Cat struct {
name string
}
type SomethingGeneric struct {
getSomething func() interface{}
}
func getSomeCat() interface{} {
return Cat{}
}
var somethingForCats = SomethingGeneric{getSomething: getSomeCat}
func TestCanGetStructPointer(t *testing.T) {
interfaceVariable := somethingForCats.getSomething()
pointer := &interfaceVariable
interfaceVarType := reflect.TypeOf(interfaceVariable)
structPointerType := reflect.PtrTo(interfaceVarType)
pointerType := reflect.TypeOf(pointer)
if pointerType != structPointerType {
t.Errorf("Pointer type was %v but expected %v", pointerType, structPointerType)
}
}
The test fails with:
Pointer type was *interface {} but expected *parameterized.Cat
#dyoo's example does work, but it relies on you to manually cast Dog and Cat.
Here's a bit of a convoluted/verbose example which avoids that constraint somewhat:
package main
import (
"fmt"
"reflect"
)
type Cat struct {
name string
}
type SomethingGeneric struct {
getSomething func() interface{}
}
func getSomeCat() interface{} {
return Cat{name: "Fuzzy Wuzzy"}
}
var somethingForCats = SomethingGeneric{getSomething: getSomeCat}
func main() {
interfaceVariable := somethingForCats.getSomething()
castVar := reflect.ValueOf(interfaceVariable)
castVar.Convert(castVar.Type())
// If you want a pointer, do this:
fmt.Println(reflect.PtrTo(castVar.Type()))
// The deref'd val
if castVar.Type() != reflect.TypeOf(Cat{}) {
fmt.Printf("Type was %v but expected %v\n", castVar, reflect.TypeOf(&Cat{}))
} else {
fmt.Println(castVar.Field(0))
}
}
Playground Link
I found this thread: https://groups.google.com/forum/#!topic/golang-nuts/KB3_Yj3Ny4c
package main
import (
"fmt"
"reflect"
)
type Cat struct {
name string
}
//
// Return a pointer to the supplied struct via interface{}
//
func to_struct_ptr(obj interface{}) interface{} {
fmt.Println("obj is a", reflect.TypeOf(obj).Name())
// Create a new instance of the underlying type
vp := reflect.New(reflect.TypeOf(obj))
// Should be a *Cat and Cat respectively
fmt.Println("vp is", vp.Type(), " to a ", vp.Elem().Type())
vp.Elem().Set(reflect.ValueOf(obj))
// NOTE: `vp.Elem().Set(reflect.ValueOf(&obj).Elem())` does not work
// Return a `Cat` pointer to obj -- i.e. &obj.(*Cat)
return vp.Interface()
}
//
// Dump out a pointer ...
//
func test_ptr(ptr interface{}) {
v := reflect.ValueOf(ptr)
fmt.Println("ptr is a", v.Type(), "to a", reflect.Indirect(v).Type())
}
func main() {
cat := Cat{name: "Fuzzy Wuzzy"}
// Reports "*main.Cat"
test_ptr(&cat)
// Get a "*Cat" generically via interface{}
sp := to_struct_ptr(cat)
// *should* report "*main.Cat" also
test_ptr(sp)
fmt.Println("sp is",sp)
}
The following may help: http://play.golang.org/p/XkdzeizPpP
package main
import (
"fmt"
)
type Cat struct {
name string
}
type Dog struct {
name string
}
type SomethingGeneric struct {
getSomething func() interface{}
}
func getSomeCat() interface{} {
return Cat{name: "garfield"}
}
func getSomeDog() interface{} {
return Dog{name: "fido"}
}
var somethings = []SomethingGeneric{
SomethingGeneric{getSomething: getSomeCat},
SomethingGeneric{getSomething: getSomeDog},
}
func main() {
for _, something := range somethings {
interfaceVariable := something.getSomething()
cat, isCat := interfaceVariable.(Cat)
dog, isDog := interfaceVariable.(Dog)
fmt.Printf("cat %v %v\n", cat, isCat)
fmt.Printf("dog %v %v\n", dog, isDog)
}
}
I have tried
func (m map[string]interface{}) Foo() {
...
}
and
func (m *map[string]interface{}) Foo() {
...
}
but go test errors with
invalid receiver type map[string]interface {} (map[string]interface {} is an unnamed type)
so I have to add some more text to keep SO happy here
You need to define a new type to be able to attach a method to it.
package main
import "fmt"
type MyMap map[string]interface{}
func (m MyMap) Foo() {
fmt.Println("You fool!")
}
func main(){
m := new(MyMap)
m.Foo()
}