Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

How to properly use .Call in reflect package

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!

like image 746
user1493543 Avatar asked Dec 21 '13 02:12

user1493543


2 Answers

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)
like image 159
nemo Avatar answered Nov 27 '22 13:11

nemo


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.)

like image 34
twotwotwo Avatar answered Nov 27 '22 14:11

twotwotwo