问题
I'm implementing a go module that loads go plugins.
I'm assuming a function with a certain name and a certain signature exists on the main package, and would like to have a nice error message in case it is not found or not matching the expected signature.
Given a variable with a function type, how can one get the underlying signature of that function?
The following only prints the type's name (e.g. main.ModuleInitFunc
) and not the full signature.
package main
import "fmt"
type ModuleInitFunc func(someInt int) error
func main() {
var myFunc ModuleInitFunc = nil
fmt.Printf("%T", lol)
}
回答1:
reflect.Type.String()
only returns the type name, so if the function value has a named type, you'll only see the type name. Note that this will print the function's signature if the function value is a function literal (has an unnamed type):
var myFunc ModuleInitFunc
fmt.Printf("%T\n", myFunc)
fmt.Printf("%T\n", func(i int) error { return nil })
Output (try it on the Go Playground):
main.ModuleInitFunc
func(int) error
If the type is a named type, we have to construct the signature ourselves, but fortunately the reflect.Type has all the information we need for that.
Type.In()
returns the type of the ith parameter, and similarly Type.Out()
returns the type of the ith result type.
Using those, here's an example implementation that returns the signature of a function value:
func signature(f interface{}) string {
t := reflect.TypeOf(f)
if t.Kind() != reflect.Func {
return "<not a function>"
}
buf := strings.Builder{}
buf.WriteString("func (")
for i := 0; i < t.NumIn(); i++ {
if i > 0 {
buf.WriteString(", ")
}
buf.WriteString(t.In(i).String())
}
buf.WriteString(")")
if numOut := t.NumOut(); numOut > 0 {
if numOut > 1 {
buf.WriteString(" (")
} else {
buf.WriteString(" ")
}
for i := 0; i < t.NumOut(); i++ {
if i > 0 {
buf.WriteString(", ")
}
buf.WriteString(t.Out(i).String())
}
if numOut > 1 {
buf.WriteString(")")
}
}
return buf.String()
}
Testing it:
var myFunc ModuleInitFunc
fmt.Println(signature(func(i int) error { return nil }))
fmt.Println(signature(myFunc))
fmt.Println(signature(time.Now))
fmt.Println(signature(os.Open))
fmt.Println(signature(log.New))
fmt.Println(signature(""))
Output (try it on the Go Playground):
func (int) error
func (int) error
func () time.Time
func (string) (*os.File, error)
func (io.Writer, string, int) *log.Logger
<not a function>
Note that it is not possible to also print the names of the parameters and result types, because that is not stored / accessible. For details, see Is unnamed arguments a thing in Go?
来源:https://stackoverflow.com/questions/54129042/how-to-get-a-functions-signature-as-string-in-go