Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Get pointer to value using reflection

Tags:

reflection

go

I have a function that iterates through all fields of an interface passed as parameter. In order to achieve this is I am using reflection. The issue is that I do not know how to obtain the address of a non-pointer field. Here is an example:

type Z struct {
    Id int
}

type V struct {
    Id int
    F Z
}

type T struct {
    Id int
    F V
}

The above code represents my test structures. Now here is the actual function which traverses a specified structure and lists details about it:

func InspectStruct(o interface{}) {
     val := reflect.ValueOf(o)
     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()
     }

    for i := 0; i < val.NumField(); i++ {
        valueField := val.Field(i)
        typeField := val.Type().Field(i)
        address := "not-addressable"

        if valueField.Kind() == reflect.Interface && !valueField.IsNil() {
            elm := valueField.Elem()
            if elm.Kind() == reflect.Ptr && !elm.IsNil() && elm.Elem().Kind() == reflect.Ptr {
                valueField = elm
            }
        }
        if valueField.Kind() == reflect.Ptr {
            valueField = valueField.Elem()
        }
        if valueField.CanAddr() {
            address = fmt.Sprint(valueField.Addr().Pointer())
        }

        fmt.Printf("Field Name: %s,\t Field Value: %v,\t Address: %v\t, Field type: %v\t, Field kind: %v\n", typeField.Name, 
            valueField.Interface(), address, typeField.Type, valueField.Kind())

        if valueField.Kind() == reflect.Struct {
            InspectStruct(valueField.Interface())
        }
    }
}

And here is the actual test after structure instantiation/initialization:

t := new(T)
t.Id = 1
t.F = *new(V)
t.F.Id = 2
t.F.F = *new(Z)
t.F.F.Id = 3

InspectStruct(t)

And finally the output of InspectStruct call:

Field Name: Id,  Field Value: 1,     Address: 408125440 , Field type: int   , Field kind: int
Field Name: F,   Field Value: {2 {3}},   Address: 408125444 , Field type: main.V    , Field kind: struct
Field Name: Id,  Field Value: 2,     Address: not-addressable   , Field type: int   , Field kind: int
Field Name: F,   Field Value: {3},   Address: not-addressable   , Field type: main.Z    , Field kind: struct
Field Name: Id,  Field Value: 3,     Address: not-addressable   , Field type: int   , Field kind: int

As you can see I am using recursion, so if one of the fields is a struct kind then I call InspectStruct for it. My issue is that though all fields have been initialized for the entire structure "t" hierarchy, I am not able to get the address for any field located at a higher depth than "t". I would really appreciate any help.

like image 470
Mihai H Avatar asked Jun 22 '14 04:06

Mihai H


2 Answers

Passing reflect.Value instead of interface{} seems to fix the problem, however I don't know why valueField.Interface() doesn't work.

Working example : http://play.golang.org/p/nleA2YWMj8

func InspectStructV(val reflect.Value) {
    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()
    }

    for i := 0; i < val.NumField(); i++ {
        valueField := val.Field(i)
        typeField := val.Type().Field(i)
        address := "not-addressable"

        if valueField.Kind() == reflect.Interface && !valueField.IsNil() {
            elm := valueField.Elem()
            if elm.Kind() == reflect.Ptr && !elm.IsNil() && elm.Elem().Kind() == reflect.Ptr {
                valueField = elm
            }
        }

        if valueField.Kind() == reflect.Ptr {
            valueField = valueField.Elem()

        }
        if valueField.CanAddr() {
            address = fmt.Sprintf("0x%X", valueField.Addr().Pointer())
        }

        fmt.Printf("Field Name: %s,\t Field Value: %v,\t Address: %v\t, Field type: %v\t, Field kind: %v\n", typeField.Name,
            valueField.Interface(), address, typeField.Type, valueField.Kind())

        if valueField.Kind() == reflect.Struct {
            InspectStructV(valueField)
        }
    }
}

func InspectStruct(v interface{}) {
    InspectStructV(reflect.ValueOf(v))
}
like image 149
OneOfOne Avatar answered Oct 04 '22 19:10

OneOfOne


The reason Interface() doesn't work is because of the interface wrapper it returns. To give an idea of what's going on, let's look at what we're doing without reflection:

type MyStruct struct {
    F Foo
}

type Foo struct {
    i int
}

func ExtractField(ptr *MyStruct) interface{} {
    return ptr.F
}

func main() {
    ms := &MyStruct{Foo{5}}
    f := ExtractField(ms).(Foo) // extract value
    f.i = 19
    fmt.Println(f, ms.F)            // ???
    fmt.Println(&f == &ms.F)        // Not the same!
}

(Playground)

However, think about the interface{} this returns. What is it wrapping? The value of ptr.F -- that is, a copy of it. This is what value.Interface does, it returns you the interface{} wrapping the field. There is no longer any pointer metadata, it's completely detached from the original struct.

As you'll note, passing a value directly to reflect.ValueOf will always return false for CanAddr for the "top tier" -- because that address is meaningless, since it would give you the address of the copy of the value, changing it wouldn't really mean anything. (Keep in mind that pointers are values too -- if you want the address of a pointer-valued field like *Foo, you're really looking for **Foo).

So, in our example above, if we were to naively pass in reflect.ValueOf(ExtractField(ms)) we'd get the ValueOf f, which not only doesn't have the address you want -- it isn't even addressable according to reflect because it would never give a valid address as far as reflect is concerned (the only address it could give you is the address of the internal value copy in the Value struct).

So why does passing the Value down the rabbit hole work? Well, the only real way to say it is that reflect.Value maintains the necessary metadata when you use Elem and Field, while the interface{} cannot. So while the reflect.Value may look like:

// Disclaimer: not the real structure of a reflect.Value
type Value struct {
    fieldAddress uintptr
    value        Foo
}

All it can give you is this

// Again, an abstraction of the real interface wrapper 
// just for illustration purposes
type interface{} struct {
    value Foo
}
like image 24
Linear Avatar answered Oct 04 '22 20:10

Linear