The golang’s function is a code block like C’s, but it can also be assigned to a variable as its other types.
If you are not familiar with the function, Codewalk: First-Class Functions in Go should be a good starting point for you. Already known it? Let’s go on.
First of all, look at this PHP codes:
function foobar() { echo "Hello Golang\n"; } $funcs = array( "foobar" => "foobar", "hello" => "foobar", ); $funcs["foobar"](); $funcs["hello"]();
It will print:
mikespook@mikespook-laptop:~/Desktop$ php foobar.php Hello Golang Hello Golang
It is very useful for calling a function with name matching.
So, is it possible to call a function by its name in Golang?
As a static, compiled programming language, the answer is No … and YES!
You can not do this in Golang:
func foobar() { // bla...bla...bla... } funcname := "foobar" funcname()
You can do:
func foobar() { // bla...bla...bla... } funcs := map[string]func() {"foobar":foobar} funcs["foobar"]()
But here’s a limitation that the map only work with the prototype “func()”, no input parameters and return arguments.
If you want to call some functions have different function’s prototypes, the interface{} should be used.
Yep! interface{}, like the void pointer in C. Remember that? No? Never mind! Read this: The Go Programming Language Specification:Interface types.
Then we could add functions with different prototypes into one map:
func foo() { // bla...bla...bla... } func bar(a, b, c int) { // bla...bla...bla... } funcs := map[string]interface{}{"foo":foo, "bar":bar}
How to call a function in the map? like this?
funcs["foo"]()
NO! It does not work! You can not call a function stored in a empty interface variable directly.
Dadadada…
Reflection comes to us! It is a package called “reflect” in Golang. Do you know reflection already?
If not, just read this: Laws of reflection.
func Call(m map[string]interface{}, name string, params ... interface{}) (result []reflect.Value, err error) { f = reflect.ValueOf(m[name]) if len(params) != f.Type().NumIn() { err = errors.New("The number of params is not adapted.") return } in := make([]reflect.Value, len(params)) for k, param := range params { in[k] = reflect.ValueOf(param) } result = f.Call(in) return } Call(funcs, "foo") Call(funcs, "bar", 1, 2, 3)
Reflecting the function variable, use reflect.Call to call it and pass parameters into it at the same time.
Nothing could be hard to understand.
I’ve done a package for this functional: https://bitbucket.org/mikespook/golib/src/27c65cdf8a77/funcmap.
Hope this helps. Have a good time, gophers!
所有涉及到go的函数均应该为func关键字而非function:比如:
function foo() {
// bla…bla…bla…
}
function bar(a, b, c int) {
// bla…bla…bla…
}
Fixed, thx!
How about:
package mypackage
func foo (s string, i int) (bool, error) {
return false, nil
}
func bar (opts []string) (int, error) {
return 456, nil
}
var Foo func(string, int) (bool, error)
var Bar func([]string) (int, error)
func init() {
Foo = foo
Bar = bar
}
func main() {
b, err := Foo(“abc”, 345)
i, err := Bar([]string{“abc”, “edf”})
}
You could use this for exporting private functions only for testing f.i..
Johan
Possible func variable value default?
func foo (s string = “test”, i int = 1) (bool, error)
How to do? its not work
There is no default variable value for functions in Golang. If you want to do so, the `reflect` package may be the only choice for you. You can write a warp function which detects if the params are zero value, and then set a default value.
Good article. I’m getting this error in your last example:
cannot use funcs (type map[string]func()) as type map[string]interface {} in argument to Call
https://gist.github.com/yaxinr/679976f1cde2a7b2925c30fa89b03eea
// Invoke – firstResult, err := invoke(AnyStructInterface, MethodName, Params…)
func Invoke(any interface{}, name string, args …interface{}) (reflect.Value, error) {
method := reflect.ValueOf(any).MethodByName(name)
methodType := method.Type()
numIn := methodType.NumIn()
if numIn > len(args) {
return reflect.ValueOf(nil), fmt.Errorf(“Method %s must have minimum %d params. Have %d”, name, numIn, len(args))
}
if numIn != len(args) && !methodType.IsVariadic(){
return reflect.ValueOf(nil), fmt.Errorf(“Method %s must have %d params. Have %d”, name, numIn, len(args))
}
in := make([]reflect.Value, len(args))
for i := 0; i = numIn-1 {
inType = methodType.In(numIn – 1).Elem()
} else {
inType = methodType.In(i)
}
argValue := reflect.ValueOf(args[i])
argType := argValue.Type()
if argType.ConvertibleTo(inType) {
in[i] = argValue.Convert(inType)
} else {
return reflect.ValueOf(nil), fmt.Errorf(“Method %s. Param[%d] must be %s. Have %s”, name, i, inType, argType)
}
}
return method.Call(in)[0], nil
}
Thanks! This is the way to call methods of a struct by name.
However, it won’t work with normal functions.
BTW, the last return statement `method.Call(in)[0]`, the element length should be checked if equals 0 or not.