How to properly use .Call in reflect package
Asked Answered
D

2

8

Been having one last issue with my code which involves the .Call function in the reflect package.

So I'm making a call such as this:

params := "some map[string][]string"
in := make([]reflect.Value,0)
return_values := reflect.ValueOf(&controller_ref).MethodByName(action_name).Call(in)

where the method I'm making the .Call to is as follows:

func (c *Controller) Root(params map[string][]string) map[string] string{}

What I don't quite understand is how to manipulate the "in" variable in order to properly pass the map I need to into the function. I see that the second parameter in the make() is the length of the parameter? But I don't quite understand how to format the vars in order to properly pass in my parameter. I am recursively running into the error message:

reflect: Call with too few input arguments

Any help would be much appreciated!

Duleba answered 21/12, 2013 at 2:57 Comment(0)
I
16

From the Value.Call documentation:

Call calls the function v with the input arguments in. For example, if len(in) == 3, v.Call(in) represents the Go call v(in[0], in[1], in[2]).

So if you want to call a function with one parameter, in must contain one reflect.Value of the right type, in your case map[string][]string.

The expression

in := make([]reflect.Value,0)

creates a slice with length 0. Passing this to Value.Call will result in the panic you receive as you need 1 parameter, not zero.

The correct call would be:

m := map[string][]string{"foo": []string{"bar"}}

in := []reflect.Value{reflect.ValueOf(m)}

myMethod.Call(in)
Isoagglutinin answered 21/12, 2013 at 3:24 Comment(1)
Ah ok, ok I see, thanks so much! :) Appreciate all your help today!Duleba
H
11

The call is trying to pass zero parameters to a controller that expects one param (in is an empty slice). You need to do something more like in := []reflect.Value{reflect.ValueOf(params)}.

You could also call .Interface() once you've found the method, then use type assertion to get a func you can call directly:

// get a reflect.Value for the method
methodVal := reflect.ValueOf(&controller_ref).MethodByName(action_name)
// turn that into an interface{}
methodIface := methodVal.Interface()
// turn that into a function that has the expected signature
method := methodIface.(func(map[string][]string) map[string]string)
// call the method directly
res := method(params)

(Then you could even cache method in a map keyed by method name, so you wouldn't have to do reflect operations next call. But you don't have to do that for it to work.)

Hutchins answered 21/12, 2013 at 3:23 Comment(5)
I don't understand your last paragraph.Isoagglutinin
Sorry. Added code. I'm saying that once you find the method, get an interface{} and do the rest with a type assertion.Hutchins
(@nemo--does that clear up what I was saying at all? Even if you may not think it's a good idea?)Hutchins
+1, now it makes sense to me :) That's not a bad idea, however you'd need to know the type at compile time which is not always possible.Isoagglutinin
Thanks a lot for this answer. Helps me to fixing my own problem: #27674247Sexual

© 2022 - 2024 — McMap. All rights reserved.