I want to have a generic way which will always return the struct value no matter if it is provided as pointer, slice or array.
My approach towards this looks:
func main() {
p := Person{}
if value(p).Kind() != reflect.Struct {
fmt.Printf("Error 1")
}
if value(&p).Kind() != reflect.Struct {
fmt.Printf("Error 2")
}
if value([]Person{p}).Kind() != reflect.Struct {
fmt.Printf("Error 3")
}
if value(&[]Person{p}).Kind() != reflect.Struct {
fmt.Printf("Error 4")
}
}
func value(m interface{}) reflect.Value {
v := reflect.ValueOf(m)
switch v.Kind() {
case reflect.Ptr:
v = v.Elem()
fallthrough
case reflect.Slice, reflect.Array:
v = v.Elem()
}
return v
}
Go Playground
As you can see the problem lays with in getting the struct out of a slice or array.
How do I need to extend the above function to get the struct value from with in an array or slice?
Update: What I want to do is turn []People into People.
If you just want the type even if the slice is nil, you can use something like this:
func value(m interface{}) reflect.Type {
t := reflect.Indirect(reflect.ValueOf(m)).Type()
if t.Kind() == reflect.Slice || t.Kind() == reflect.Array {
t = t.Elem()
if t.Kind() == reflect.Ptr {
t = t.Elem()
}
return t
}
return t
}
About Type.Elem(), from http://golang.org/pkg/reflect/#Type:
// Elem returns a type's element type.
// It panics if the type's Kind is not Array, Chan, Map, Ptr, or Slice.
//edit updated the function to work on a slice of pointers as well.
I assume that what you mean by "get out of the slice or array" is that you want the first element (that is, the element at index 0)? If that's what you want, then you should use the reflect.Value.Index() method. For example:
func value(m interface{}) reflect.Value {
v := reflect.ValueOf(m)
switch v.Kind() {
case reflect.Ptr:
v = v.Elem()
if v.Kind() == reflect.Slice || v.Kind() == reflect.Array {
v = v.Index(0)
}
case reflect.Slice, reflect.Array:
v = v.Index(0)
default:
break LOOP
}
return v
}
Go playground
Note that I also slightly modified the flow logic. You were falling through to the slice/array case from the pointer case. You probably intended for the case condition to be tested again (so it'd effectively say, "if this was a pointer, now check if the thing it pointed to was a slice or an array"), but that's not how fallthrough works. Now it checks the case explicitly.
Related
I just can't find the way to get a slice of pointer to each attribute of a given struct. I am using reflection to get my pointers (Thanks to https://stackoverflow.com/a/24348352/6093604)
if valueField.CanAddr() {
address = fmt.Sprintf("0x%X", valueField.Addr().Pointer())
}
As you can see, valueField.Addr().Pointer() returns a pointer addr value, however, using reflection, I would like to get a usable pointer for sql.Rows.Scan()
So what did I do is:
func StructAttributesToPointersSlice(object interface{}) []interface{} {
val := reflect.ValueOf(object)
if val.Kind() == reflect.Interface && !val.IsNil() {
elm := val.Elem()
if elm.Kind() == reflect.Ptr && !elm.IsNil() && elm.Elem().Kind() == reflect.Ptr {
val = elm
}
}
if val.Kind() == reflect.Ptr {
val = val.Elem()
}
var ptrs []interface{}
for i := 0; i < val.NumField(); i++ {
valueField := val.Field(i)
if valueField.Kind() == reflect.Ptr {
valueField = valueField.Elem()
}
if valueField.CanAddr() {
ptrs = append(ptrs, valueField.Addr().Pointer())
}
}
return ptrs
}
But when I try to use it for Scan() sql function:
var values []interface{}
// Iterate over each rows got from the query
for rows.Next() {
ptrs := utils.StructAttributesToPointersSlice(&newObject)
for _, item := range ptrs {
fmt.Println(reflect.TypeOf(item))
}
err = rows.Scan(ptrs...)
if err != nil {
return nil, model.Status{Code: http.StatusInternalServerError, Error: err.Error()}
} else {
values = append(values, newObject)
}
}
I am getting this error:
sql: Scan error on column index 0: destination not a pointer
I know it's because it's not the good type since it's a uintptr, but then how to transform it into usable pointer?
Thanks
Use unsafe.Pointer to convert a uintptr to a pointer of some type. As an example, the following expression converts uintptr u to a T pointer:
(*T)(unsafe.Pointer(u))
This conversion does not help in StructAttributesToPointersSlice because the struct fields can be of any type. Also, the conversion from uintptr is not needed and unsafe.
The expression valueField.Addr() is the reflect.Value for the pointer to the field. Call Interface() to get the actual pointer. To fix the program, change
ptrs = append(ptrs, valueField.Addr().Pointer())
to
ptrs = append(ptrs, valueField.Addr().Interface())
Here's a simplified version of the function:
func StructAttributesToPointersSlice(object interface{}) []interface{} {
v := reflect.ValueOf(object)
if v.Kind() != reflect.Ptr || v.Elem().Kind() != reflect.Struct {
panic("argument must be a pointer to struct")
}
v = v.Elem()
var result []interface{}
for i := 0; i < v.NumField(); i++ {
f := v.Field(i)
if !f.CanSet() {
continue
}
result = append(result, f.Addr().Interface())
}
return result
}
Some notes about this code:
The argument must be a pointer to a struct.
There's no need to call CanAddr on the fields. The check for pointer to struct covers this.
The CanSet() skips over unexported fields. You may want it to panic instead.
The function panics for errors in the caller. Consider returning an error instead.
playground example
Given
// I know that behind SomeInterface can hide either int or a pointer to struct
// In the real code I only have v, not i
i := something.(SomeInterface)
v := reflect.ValueOf(i)
var p uintptr
if "i is a pointer to struct" {
p = ???
}
I need some way to distinguish between pointers and values in this situation.
If i was a pointer to struct, I need to cast it to uintptr.
Something I discovered so far: the second member of the (*reflect.Value).InterfaceData() will be the pointer to the struct, in case it is a struct. I have no idea what it is in case it is not a struct.
Use the Pointer method to get the address of a struct as a uintpr:
var p uintptr
if v.Kind() == reflect.Ptr && v.Elem().Kind() == reflect.Struct {
p = v.Pointer()
}
playground example
This code assumes that v is the result of calling reflect.ValueOf(i) as shown in the question. In this scenario, v represents i's element, not i. For example, if interface i contains an int, then v.Kind() is reflect.Int, not reflect.Interface.
If v has an interface value, then drill down through the interface to get the uintptr:
if v.Kind() == reflect.Interface {
v = v.Elem()
}
var p uintptr
if v.Kind() == reflect.Ptr && v.Elem().Kind() == reflect.Struct {
p = v.Pointer()
}
playground example
I have a struct and I am trying to iterate through all fields to determine whether or not the field is a map (or a pointer to a map). I'm able to compare field kind to reflect.Map for non-pointer fields, but I am having trouble doing the same for pointer fields. If the field has a value I can use Elem() to determine the pointer field type, but if the field is nil that method won't work and I get "invalid." Any ideas on how to accomplish this? Playground is here.
package main
import (
"fmt"
"reflect"
)
type Item struct {
itemMap map[string]interface{}
itemMapPtr *map[string]interface{}
}
func main() {
item := new(Item)
printTypes(*item)
}
func printTypes(item Item) {
itemVal := reflect.ValueOf(item)
for i := 0; i < itemVal.NumField(); i++ {
fieldVal := itemVal.Field(i)
if fieldVal.Kind() == reflect.Ptr {
fieldVal = fieldVal.Elem() // This is only helpful if the field is not nil
}
fmt.Println(fieldVal.Kind())
}
}
Work with types instead of values:
t := reflect.TypeOf(item)
for i := 0; i < t.NumField(); i++ {
ft := t.Field(i).Type
if ft.Kind() == reflect.Ptr {
ft = ft.Elem()
}
fmt.Println(ft.Kind())
}
playground example
I have a function
func (r *render) foo(v interface{}) {
val := reflect.ValueOf(v)
fields := structs.Fields(val.Index(0).Interface())
...
Which takes a slice of structs and tries to get the fields of v,
however if v is empty then "val.Index(0)" crashes the program. Is there a better way to do this?
You need to check first if you have a slice to begin with, then check if you have an empty slice, and you probably should check that you have a struct too while you're at it: (example)
val := reflect.ValueOf(v)
if val.Kind() != reflect.Slice {
fmt.Println("not a slice")
return
}
if val.Len() == 0 {
fmt.Println("empty slice")
return
}
if val.Index(0).Kind() != reflect.Struct {
fmt.Println("not a slice of structs")
return
}
fields := structs.Fields(val.Index(0).Interface())
...
If you only want the fields from a struct type, regardless of if the slice is empty, you can use the slice type's Elem method to extract it (example)
// get the internal type of the slice
t := val.Type().Elem()
if t.Kind() != reflect.Struct {
fmt.Println("not a struct")
return
}
fmt.Println("Type:", t)
for i := 0; i < t.NumField(); i++ {
fmt.Println(t.Field(i).Name)
}
I have the following query builder function:
func CreateQuery(t interface{}, where string) {
var b bytes.Buffer
b.WriteString("SELECT ")
s := reflect.ValueOf(t).Elem()
typeOfT := s.Type()
for i := 0; i < s.NumField() - 1; i++ {
b.WriteString(fmt.Sprintf("%s, ", typeOfT.Field(i).Name))
}
//Last one has no Comma
b.WriteString(fmt.Sprintf("%s ", typeOfT.Field(s.NumField() - 1).Name))
b.WriteString(fmt.Sprintf("FROM %s ", typeOfT.Name()))
b.WriteString(where)
fmt.Println(b.String())
}
There works fine when called as follows:
var dst FooStruct
CreateQuery(&dst, "")
But the following raises a "call of reflect.Value.NumField on slice Value" Panic:
var dst []FooStruct
CreateQuery(&dst, "")
How I can I make the function print the fields of a slice's underlying struct type? It seems like I want the inverse of reflect's SliceOf function.
You can only call NumField or Field methods on a reflect.Type representing a struct (i.e. t.Kind() == reflect.Struct).
If you have a slice type, you can access the contained type via the Elem method, which returns another reflect.Type. If the slice contains a struct, then you can call NumField/Field on this type.
You can iterate over the slice, calling CreateQuery for every query:
func CreateQueries(t interface{}, where string) {
v := reflect.ValueOf(t)
if v.Kind() == reflect.Ptr {
v = v.Elem()
}
if v.Kind() == reflect.Array || v.Kind() == reflect.Slice {
for i := 0; i < v.Len(); i++ {
CreateQuery(v.Index(i).Interface(), where)
}
}
}
Using reflect.Value.Index you can access each field separately, calling .Interface() on the
value yields the interface{} type representation of that value, making it suitable to put
it in your CreateQuery function (which expects a interface{} value).