Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Invoke a function which is received as an interface variable in golang

I have a code which is similar to the following

package main

import "fmt"

func PrintThis(arg string) {
    fmt.Printf("I'm printing %s", arg)
}

func PrintThisAndThat(arg1, arg2 string) {
    fmt.Printf("Now printing %s and %s", arg1, arg2)
}

func Invoke(fn interface{}, args ...string) {
    //fn(args...)
}

func main() {
    Invoke(PrintThis, "foo")
    Invoke(PrintThisAndThat, "foo", "bar")
}

This is not the actual production code, but this is a simplified version.

Question :- If I uncomment the line //fn(args...) I get a compile error prog.go:14: cannot call non-function fn (type interface {})

How do I execute the function which is received as the argument tho the Invoke() function?

What is the right way to achieve this?

like image 536
Manu Viswam Avatar asked Mar 02 '16 17:03

Manu Viswam


2 Answers

You use the Call or CallSlice methods of the reflect.Value to call it as a function. As with all reflect.Value methods, this panics is fn is the wrong type.

func Invoke(fn interface{}, args ...string) {
    v := reflect.ValueOf(fn)
    rargs := make([]reflect.Value, len(args))
    for i, a := range args {
        rargs[i] = reflect.ValueOf(a)
    }
    v.Call(rargs)
}

http://play.golang.org/p/xGmNLDcLL_

like image 85
JimB Avatar answered Oct 21 '22 14:10

JimB


You can use a type switch like so http://play.golang.org/p/opotbIGdrA

package main

import "fmt"

func PrintThis(arg string) {
    fmt.Printf("I'm printing %s", arg)
}

func PrintThisAndThat(arg1, arg2 string) {
    fmt.Printf("Now printing %s and %s", arg1, arg2)
}

func Invoke(fn interface{}, args ...string) {
    switch m := fn.(type) {
    case func(string):
        m(args[0])
    case func(string, string):
        m(args[0], args[1])
    default:

    }
}

func main() {
    Invoke(PrintThis, "foo")
    Invoke(PrintThisAndThat, "foo", "bar")
}

But you kind of need to know what functions will be passed to make this work properly.

Btw you can turn PrintThis into a variadic function by using ...string instead of multiple string arguments.

like image 36
Datsik Avatar answered Oct 21 '22 14:10

Datsik