How to get a function's signature as string in go

﹥>﹥吖頭↗ 提交于 2021-02-05 07:14:45

问题


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

标签
易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!