Reputation: 38897
Given an interface containing functions with varying parameters and a concrete implementation of that interface how do I take a stream of bytes and call methods from the interface based on that byte stream. Basically an RPC call.
e.g.
type funcs interface {
func Hello(name string) (string, error)
func Add(numbers...int) (result, error)
}
And lets say I had the following input data
Hello John
Add 1 2 3 4
I figure this has to be done with reflection but this package I find a bit of mystery to use. Not great examples. Looking at RPC packages is also a bit mysterious. I don't understand what they are doing and there seem to be limitations or assumptions, perhaps to make the code simpler and/or faster to implement.
Can someone shed some light on how I might take the string "Hello" and find the function handle "Hello" from the funcs table. And know that it's expecting a single parameter that is a string and then calling that function with the argument provided "John".
Upvotes: 1
Views: 321
Reputation: 4548
You can do this using the reflect
package (general caveats about performance and code readability apply). Just create a type and implement the functions for that type, then call reflect.MethodByName() on a Value
created from an instance of that type:
package main
import (
"fmt"
"reflect"
)
type functions struct {}
func (f functions) Hello(name string) (string, error) {
fmt.Println("Hello,", name)
return name, nil
}
func main() {
var f functions
m := reflect.ValueOf(f).MethodByName("Hello")
if m.IsValid() {
// Either call f.Hello() here, or use the returned Value like so:
v := make([]reflect.Value, 1) // function Values only take Value slices as arguments
v[0] = reflect.ValueOf("Asha")
r := m.Call(v)
fmt.Println("Returned", r)
}
}
This prints:
Hello, Asha
Returned [Asha <error Value>]
(See https://play.golang.org/p/0q1i3WBuAL)
However, if you know the function names you want to be able to call a priori, or if you want the user to be able to use names other than the exact function name, just use a switch
or if
/else
statement:
package main
import (
"fmt"
"strings"
)
func Hello(name string) {
fmt.Println("Hello,", name)
}
func main() {
userInput := make([]string, 2)
userInput[0] = "HeLlO"
userInput[1] = "재은"
switch(strings.ToLower(userInput[0])) { // allow any case
case "hello":
Hello(userInput[1])
default:
panic("user input not recognized")
}
}
This prints:
Hello, 재은
(See https://play.golang.org/p/oBxsI42q4a)
Upvotes: 1