I'm trying to do this:
if event.Type == sdl.QUIT {
utils.Running = false
}
But I can't because when I try to build, I get this error:
./mm.go:11: event.Type undefined (type sdl.Event has no field or method Type)
Here is the relevant source code of the library I'm trying to use:
type Event interface{}
type CEvent struct {
Type uint32
_ [52]byte // padding
}
type CommonEvent struct {
Type uint32
Timestamp uint32
}
// WindowEvent (https://wiki.libsdl.org/SDL_WindowEvent)
type WindowEvent struct {
Type uint32
Timestamp uint32
WindowID uint32
Event uint8
_ uint8 // padding
_ uint8 // padding
_ uint8 // padding
Data1 int32
Data2 int32
}
As you can see, all of the other Events have the field Type. How can I access this?
Solution
This how I ended up polling events in this SDL2 binding for Go, in case anyone is wondering:
func PollEvents() {
for {
if event := sdl.PollEvent(); event != nil {
switch event.(type) {
case *sdl.QuitEvent:
utils.Running = false
}
} else {
break
}
}
}
You actually can't. Interfaces only define a method set that is available on a type, they do nothing to expose fields. In your case I would recommend doing a type switch. It would look a little like this;
switch v := myInstance.(type) {
case CEvent:
fmt.Println(v)
case CommonEvent:
fmt.Println(v)
case WindowEvent:
fmt.Println(v)
default:
fmt.Println("unknown")
}
You may want to structure your code a bit differently depending on what you're doing with the instance after this but that gives you the basic idea. You can also do a type assertion with a single type like; v, err := myInstance.(CommonEvent) but I doubt it would be as effective here. It also returns an error if the type of myInstance is not CommonEvent so it's not really the best way to go about figuring out what type and interface instance may be.
You will need to know the type. Let's say we know it's a CEvent:
cEvent, ok := Event.(CEvent)
if !ok {
// You lied, not a CEvent
return
}
// Otherwise, you can get the type!
fmt.Println(cEvent.Type)
Of course if you don't know the type, you can keep type asserting until you get it right. Otherwise, throw an error, return a default value, etc:
func getType(i interface{}) uint32 {
cEvent, ok := i.(CEvent)
if ok {
return cEvent.Type
}
commonEvent, ok := i.(CommonEvent)
if ok {
return commonEvent.Type
}
// Etc
return <default>
}
You can spend a lot of time doing reflection calls or trying to guess the type or using type switches.
Or you can just define an interface with functions that return the information you need.
For example you could do
type Event interface {
GetCommonEvent() *CommonEvent
GetWindowEvent() *WindowEvent
}
Related
Assuming we have the following structures:
type BuyerData struct {
id int
balance float64
}
type AgentData struct {
id int
buyerData BuyerData
}
type GlobalData struct {
agents map[int]AgentData
}
if I wanted to define a "insert equivalent of Java's class method" for GlobalData to return a pointer to it's value buyerData on a given id, which would be a method with the following signature:
func (globalData *GlobalData) getBuyerData(id int) *BuyerData
What would be written inside it? I'm having troubles because it's giving all sorts of errors like cannot take the address of, invalid indirect of or does not support indexing...
This is what I currently have that does not generate any compiler exception:
func (globalData *GlobalData) getBuyerData(id int) *BuyerData {
var agents *map[int]AgentData = &(globalData.agents)
var agentData AgentData = (*agents)[id]
return &(agentData.buyerData)
}
But I, honestly, don't know what I'm doing or if this is the correct way of doing it.
Assuming you'd just like to return a pointer to the value in the map in a AgentData struct indexed by the argument of the function, you likely mean to say:
func (agentsGlobalData *AgentData) getBuyerData(id int) (*BuyerData, bool) {
buyer, has := agentsGlobalData.agents[id]
return &buyer, has
}
You might also consider storing pointers to AgentData in that map instead, making your struct:
type GlobalData struct {
agents map[int]*AgentData
}
But you haven't told us enough about your use case to recommend one or the other, perhaps.
(You say callers should be able to "write" to a BuyerData, but there are not any public fields in that struct...)
You need to say something like this:
func (globalData *GlobalData) GetBuyerData(id int) *BuyerData {
if agent, found := globalData.Agents[id]; found {
return &agent.BuyerData
}
return nil
}
See it here: https://play.golang.org/p/lwXn4D1mr3J
I try to create a generic function that accepts any struct value and create a array of that struct type. Here is the code I tried. But I get the error "t is not a type". How can I implement this.
type RegAppDB struct {
nm string
data []interface{}
}
func CreateRegTable(tbl string, rec interface{}) RegAppDB {
t := reflect.TypeOf(rec)
fmt.Println(t)
return RegAppDB{"log", []t}
}
Go does not support generics, and any attempt to do something like that is not going to work out well. In your specific case, there are a couple of key problems:
You cannot use a variable as a type. Go is compile-time static typed, so anything that gets type information at runtime (i.e. reflect.TypeOf) happens too late to use the way you're trying to do it.
Equally important, your struct's field is of type []interface{}, which means the only type you can use for that field is []interface{}. []string, for example, is a different type, and cannot be assigned to that field.
I took another route. Need some beautification. But this works. So now data is an array of interface and from calling function I pass pointer to structure variables to Save function.
type RegAppDB struct {
nm string
data []interface{}
cnt int
}
// CreateRegTable creates a data structure to hold the regression result
func CreateRegTable(tbl string) *RegAppDB {
return &RegAppDB{tbl, make([]interface{}, 20), 0}
}
// Save implements saving a record Regression application DB
func (rga *RegAppDB) Save(rec interface{}) error {
rga.data[rga.cnt] = rec
rga.cnt++
return nil
}
// Show implements showing the regression table
func (rga *RegAppDB) Show() error {
fmt.Println(rga.cnt)
for i := 0; i <= rga.cnt; i++ {
fmt.Println(rga.data[i])
}
return nil
}
// Compare compares two regression table for equality
func (rga *RegAppDB) Compare(rgt *RegAppDB) bool {
return reflect.DeepEqual(rga, rgt)
}
It cannot be done for a generic type. If there are a fixed number of possible types, then you can do something like the following:
type RegAppDB struct {
nm string
data interface{}
}
func CreateRegTable(rec interface{}) RegAppDB {
switch rec.(type) {
case int:
return &RegAppDB{"log", []int{}}
case string:
return &RegAppDB{"log", []string{}}
}
return nil
}
Note: Your data in RegAppDB should be of type interface{} since []int implements interface{} but not []interface{}
I'm having a bit of trouble figuring out how to either create a struct in a switch statement or assign a type to it in a switch statement.
Here's some non-working code illustrating what I'm trying to do:
var result
switch structPickingString {
case "struct1":
result = new(struct1)
case "struct2":
result = new(struct2)
}
//unmarshall some json into the appropriate struct type
if err := json.NewDecoder(resp.Body).Decode(&result); err != nil {
log.Println(err)
}
//print out that json with a function specific to that type of struct
result.Print()
I think something involving an empty interface{} might be related to solving this issue but unfortunately I'm still a bit ignorant with golang and I'm not seeing how to make it work.
Here's a link to a slightly modified version of the code for more context: https://play.golang.org/p/Rb1oaMuvmU2
The issue is not defining the print function, it's assigning a specific type of struct to the result variable based on using the individual Print function that the struct implements.
Let me know if there's any more info I could provide.
Since you are calling .Print, you need to use an interface which has that method. I think you are looking for something like
type Printer interface {
Print()
}
func (s *struct1) Print() {
// ...
}
func (s *struct2) Print() {
// ...
}
var result Printer
switch structPickingString {
case "struct1":
result = new(struct1)
case "struct2":
result = new(struct2)
}
https://play.golang.org/p/W9r6UfeQSCz
You needs match "generical interface" with structs. See this:
//my struct1
type MyStruct1 struct {
ID int
}
//my struct2
type MyStruct2 struct {
ID int
}
//my interface
type MyStructGenerical interface {
Print()
}
//method (match with MyStructGenerical)
func (m1 MyStruct1) Print() {
println(m1.ID)
}
//method (match with MyStructGenerical)
func (m2 MyStruct2) Print() {
println(m2.ID)
}
In this way you can does assertions between structs and generical interface. See this:
//here result is an generical interface
var result MyStructGenerical = nil
//checkin
switch structPickingString {
case "struct1":
tmp := new(MyStruct1)
result = tmp
case "struct2":
tmp := new(MyStruct2)
result = tmp
}
The result is:
//unmarshall some json into the appropriate struct type
if err := json.NewDecoder(body()).Decode(&result); err != nil {
log.Println(err)
}
//print out that json with a function specific to that type of struct
tmp := result.(*MyStruct1)
tmp.Print()
// or
result.Print()
Run in: https://play.golang.org/p/nHrJnImsqNN
I think you want to use an interface like
type ResultIface interface {
Print()
}
Then you can do
var result ResultIface
switch structPickingString {
case "struct1":
result = new(struct1)
case "struct2":
result = new(struct2)
}
As long as your struct1 and struct2 fulfills the ResultIface by having a Print function it will print
I suspect I am trying to shoehorn go into behaving in an OOP way, but I don't know the go idiom to do what I want.
I have a Message struct that I use to pass data around in a client-server application:
type Message struct {
ID string `json:"id,omitempty"`
Type string `json:"type"`
Data interface{} `json:"data"`
}
the Data here can be different things, for example a number of Commands:
type Command struct {
User *types.UserInfo `json:"user"`
}
type CommandA struct {
Command
A *AData `json:"a_data"`
}
type CommandB struct {
CommandB
B *BData `json:"b_data"`
}
What I want to do is to check that the message data type is a Command and perform actions that are common to all commands, for example authorisation, all in one place and not having to type assert what type of command, calling the appropriate handler function and then do the auth, as this would result in massive code duplication.
The code below reflects what I would like to happen.
for {
select {
case m := <-in:
// what I would like to do, obviously not working as
// m.Data is not of type Command but the actual command type
if c, ok := m.Data.(msg.Command); ok {
// do auth and other common stuff
}
switch t := m.Data.(type) {
case *msg.CommandA:
go srv.handleCommandA(m.ID, t)
case *msg.CommandB:
go srv.handleCommandB(m.ID, t)
// etc etc
default:
// do something
}
}
}
How do I solve this go idiomatically?
You can define common command stuff in interface
type Commander interface{
DoCommonStuff()
}
implement it for Command struct
func (c Command) DoCommonStuff(){
//do stuff
}
and then assert
if c, ok := m.Data.(Commander); ok {
c.DoCommonStuff()
}
your other code should work unchanged
One approach is using reflection to extract common field value from the Data. In your example, since all Command has User field, we can use it to identify whether Message.Data is a command or not. If Command is not embedded to the data, simply return nil. Example code:
func GetUserInfo(v interface{}) *types.UserInfo {
vt := reflect.ValueOf(v)
if vt.Kind() == reflect.Ptr {
vt = vt.Elem()
}
if vt.Kind() != reflect.Struct {
return nil
}
u := vt.FieldByName("User")
if !u.IsValid() {
return nil
}
user, ok := u.Interface().(*types.UserInfo)
if !ok {
return nil
}
return user
}
//Call GetUserInfo then perform common operation
user := GetUserInfo(m.Data)
if user != nil {
//Do auth and other common stuff
}
If I have an interface being passed into a function, is there a way to tell if the item passed in is a struct or a pointer to a struct? I wrote this silly test to illustrate what I need to figure out.
type MyStruct struct {
Value string
}
func TestInterfaceIsOrIsntPointer(t *testing.T) {
var myStruct interface{} = MyStruct{Value: "hello1"}
var myPointerToStruct interface{} = &MyStruct{Value: "hello1"}
// the IsPointer method doesn't exist on interface, but I need something like this
if myStruct.IsPointer() || !myPointerToStruct.IsPointer() {
t.Fatal("expected myStruct to not be pointer and myPointerToStruct to be a pointer")
}
}
func isStruct(i interface{}) bool {
return reflect.ValueOf(i).Type().Kind() == reflect.Struct
}
You can test via changing type according to your needs such as reflect.Ptr. You can even get pointed value with reflect.Indirect(reflect.ValueOf(i)) after you ensured it's a pointer.
Addition:
It seems reflect.Value has a Kind method so reflect.ValueOf(i).Kind() is enough.
You could use the reflect package:
i := 42
j := &i
kindOfJ := reflect.ValueOf(j).Kind()
fmt.Print(kindOfJ == reflect.Ptr)
If you know the "real" type of the interface, you can simply use a type switch:
type MyStruct struct {
Value string
}
func TestInterfaceIsOrIsntPointer(t *testing.T) {
var myStruct interface{} = MyStruct{Value: "hello1"}
var myPointerToStruct interface{} = &MyStruct{Value: "hello1"}
// the IsPointer method doesn't exist on interface, but I need something like this
switch myStruct.(type) {
case MyStruct:
// ok
break
case *MyStruct:
// error here
break
}
switch myPointerToStruct.(type) {
case MyStruct:
// error here
break
case *MyStruct:
// ok
break
}
}
The code is longer but at least you don't need to use the reflect package.