0

I'm having trouble calling a method in my recursive reflection function. Here it is:

func setPropertiesFromFlags(v reflect.Value, viper *viper.Viper) {
    t := v.Type()
    method := v.MethodByName("Parse")
    fmt.Println(method)
    for i := 0; i < t.NumField(); i++ {
        field := t.Field(i)
        switch field.Type.Kind() {
        case reflect.Struct:
            setPropertiesFromFlags(v.Field(i), viper)
        case reflect.String:
            v.Field(i).SetString(viper.GetString(field.Tag.Get("name")))
    }
}

I'm calling the function with:

// Config struct passed to all services
type Config struct {
    common.Config
    common.ServerConfig
    common.AuthConfig
}
// Parse the thing already!
func (c *Config) Parse() {
    fmt.Println("RUN THIS THING")
}
int main() {
   setPropertiesFromFlags(reflect.ValueOf(c).Elem(), viper)
}

What I'm hoping for is to get my parse method in the place where I'm printing method and run .Call() against it. Instead it's printing out: <invalid reflect.Value> which I cannot call against.

I suppose I'm having trouble wrapping my head around the return values of each method. I know I have to use ValueOf to pull the value but it seems any permutation I try I'm getting the methods from the reflection class itself :-p sigh

5
  • 1
    I believe it's because it's declared on a pointer, the Parse method that is, but you are passing in a value to your function. That is, to fix this, declare Parse on a non-pointer. Or pass in the pointer, i.e. setPropertiesFromFlags(reflect.ValueOf(c), viper) (without .Elem()) and then when you need to manipulate the struct inside setPropertiesFromFlags call Elem there. Commented Nov 6, 2019 at 18:53
  • ... alternatively keep doing what you're doing but retrieve the method from v's "address", i.e. v.Addr().MethodByName("Parse"). Commented Nov 6, 2019 at 18:59
  • @mkopriva you beat me to the answer in these comments and your last comment is perhaps the best answer. Should you write an actual answer instead of a comment? Commented Nov 6, 2019 at 19:03
  • @iLoveReflection i'm too lazy, your answer is good enough. Commented Nov 6, 2019 at 19:06
  • @ddibiase couple demonstrations of what you can and cannot do depending on whether you have a pointer or a value. play.golang.com/p/vxyIIvrsBZt (updated) Commented Nov 6, 2019 at 19:11

1 Answer 1

2

The problem is that the method is on the pointer receiver, but the function is working with a valuer receiver. Rewrite the function to work with a pointer to a struct:

func setPropertiesFromFlags(vp reflect.Value, viper *viper.Viper) {
    method := vp.MethodByName("Parse")
    fmt.Println(method)

    v := vp.Elem()
    t := v.Type()
    for i := 0; i < t.NumField(); i++ {
        field := t.Field(i)
        switch field.Type.Kind() {
        case reflect.Struct:
            setPropertiesFromFlags(v.Field(i).Addr(), viper) // <-- take address of field here
        case reflect.String:
            v.Field(i).SetString(viper.GetString(field.Tag.Get("name")))
        }
    }
}

Call like this:

   setPropertiesFromFlags(reflect.ValueOf(c), viper) // <-- do not call Elem()
Sign up to request clarification or add additional context in comments.

1 Comment

I see now. I wasn't passing in .Addr() THAT was the major issue. Thanks gents.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.