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!

Jonathan Hall
  • 75,165
  • 16
  • 143
  • 189
user1493543
  • 375
  • 3
  • 5
  • 14

2 Answers2

15

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)
nemo
  • 55,207
  • 13
  • 135
  • 135
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.)

twotwotwo
  • 28,310
  • 8
  • 69
  • 56
  • I don't understand your last paragraph. – nemo Dec 21 '13 at 03:25
  • Sorry. Added code. I'm saying that once you find the method, get an `interface{}` and do the rest with a type assertion. – twotwotwo Dec 21 '13 at 04:30
  • (@nemo--does that clear up what I was saying at all? Even if you may not think it's a good idea?) – twotwotwo Dec 21 '13 at 05:18
  • 1
    +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. – nemo Dec 21 '13 at 15:34
  • Thanks a lot for this answer. Helps me to fixing my own problem: http://stackoverflow.com/questions/27673747/reflection-error-on-golang-too-few-arguments/27692822 – Cito Dec 29 '14 at 16:56