Created
October 21, 2021 04:15
-
-
Save protosam/7659c33168a2b353b3a9ab8cb1629281 to your computer and use it in GitHub Desktop.
This example shows how to use reflect to work with functions
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
package main | |
import ( | |
"fmt" | |
"log" | |
"reflect" | |
) | |
// a map of functions to be dynamically called... | |
var funcMap = make(map[string]interface{}) | |
// a struct that some funcs may be passed if they ask for it | |
type SharedStuff struct { | |
Somevar string | |
SomeCounter int | |
} | |
func (s *SharedStuff) SaySomething() { | |
fmt.Println("\tsomeone called SaySomething()") | |
} | |
// a sharable *SharedStuff | |
var sharedstuff = &SharedStuff{} | |
// some functions for the funcMap | |
// ------------------------------ | |
// add funcs to the funcMap | |
func init() { | |
// no args, no output | |
funcMap["myfunc1"] = func() { | |
fmt.Println("\tmyfunc1 doesn't have output but it called println") | |
} | |
// no args, just output | |
funcMap["myfunc2"] = func() string { | |
return "hello world" | |
} | |
// one arg of string, just output | |
funcMap["myfunc3"] = func(s string) string { | |
reversed_s := "" | |
for i := len(s) - 1; i >= 0; i-- { | |
reversed_s += string(s[i]) | |
} | |
return reversed_s | |
} | |
// multiple args, multiple outputs | |
funcMap["myfunc3"] = func(s string, i int, ok bool) (string, int, bool) { | |
reversed_s := "" | |
for i := len(s) - 1; i >= 0; i-- { | |
reversed_s += string(s[i]) | |
} | |
return reversed_s, i + 1, !ok | |
} | |
// a shared stuff test | |
funcMap["myfunc4"] = func(shared *SharedStuff, s string) string { | |
shared.Somevar = "set by myfunc4" | |
shared.SaySomething() | |
return s | |
} | |
// a shared stuff test | |
funcMap["myfunc5"] = func(shared *SharedStuff, s string) string { | |
return "myfunc5:shared.Somevar: " + shared.Somevar | |
} | |
} | |
// do some function analysis, call them, and return any outputs | |
func main() { | |
// iterate funcMap and investigate each one | |
for fnname, fn := range funcMap { | |
fmt.Printf("########################################\n") | |
fmt.Printf("Investigating function: %s\n", fnname) | |
// reflect the method | |
method := reflect.ValueOf(fn) | |
// make sure it's actually a func | |
if method.Kind().String() != "func" { | |
log.Fatalf("expected a func but got kind %s", method.Kind().String()) | |
} | |
// how many inputs/arguments/parameters it has | |
fmt.Printf("INPUTS: %#v\n", method.Type().NumIn()) | |
// store selected inputs | |
method_call_inputs := make([]reflect.Value, 0) | |
// iterate and inspect each input | |
for i := 0; i < method.Type().NumIn(); i++ { | |
// select an input based on the reflect.Type value provided by In() | |
selected := inputSelector(method.Type().In(i)) | |
fmt.Printf("Selected input: %#v\n", selected) | |
// store the selected input | |
method_call_inputs = append(method_call_inputs, reflect.ValueOf(selected)) | |
} | |
// call the method and get outputs | |
fmt.Printf("CALLCT: %d\n", len(method_call_inputs)) | |
outputs := method.Call(method_call_inputs) | |
// iterate all the outputs | |
fmt.Printf("OUTPUTS: %d\n", len(outputs)) | |
for x := range outputs { | |
fmt.Printf("[%d]%s: %#v\n", x, outputs[x].Kind().String(), outputs[x]) | |
} | |
// some separation between iteration output | |
fmt.Println() | |
} | |
} | |
// just an input selector for testing | |
func inputSelector(expectedType reflect.Type) interface{} { | |
if reflect.ValueOf("hello selector").Type().ConvertibleTo(expectedType) { | |
return "hello selector" | |
} | |
if reflect.ValueOf(12345).Type().ConvertibleTo(expectedType) { | |
return 12345 | |
} | |
if reflect.ValueOf(true).Type().ConvertibleTo(expectedType) { | |
return true | |
} | |
if reflect.ValueOf(3.14159).Type().ConvertibleTo(expectedType) { | |
return 3.14159 | |
} | |
if reflect.ValueOf(sharedstuff).Type().ConvertibleTo(expectedType) { | |
return sharedstuff | |
} | |
// if the expectedType is a pointer we need to reflect new on it's contents | |
if expectedType.Kind() == reflect.Ptr { | |
return reflect.New(expectedType.Elem()).Interface() | |
} | |
// fallback to reflecting new on the expectedType and passing back it's interface | |
return reflect.New(expectedType).Interface() | |
} |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment