Как получить подпись функции в виде строки в го - PullRequest
0 голосов
/ 10 января 2019

Я реализую модуль go, который загружает плагины go.

Я предполагаю, что функция с определенным именем и определенной сигнатурой существует в основном пакете и хотела бы получить хорошее сообщение об ошибке, если она не найдена или не соответствует ожидаемой сигнатуре.

Учитывая переменную с типом функции, как можно получить основную сигнатуру этой функции?

Следующее печатает только имя типа (например, main.ModuleInitFunc), а не полную подпись.

package main

import "fmt"

type ModuleInitFunc func(someInt int) error

func main() {
    var myFunc ModuleInitFunc = nil

    fmt.Printf("%T", lol)
}

1 Ответ

0 голосов
/ 10 января 2019

reflect.Type.String() возвращает только имя типа, поэтому, если значение функции имеет именованный тип, вы увидите только имя типа. Обратите внимание, что при этом будет напечатана подпись функции, если значение функции является литералом функции (имеет безымянный тип):

var myFunc ModuleInitFunc

fmt.Printf("%T\n", myFunc)
fmt.Printf("%T\n", func(i int) error { return nil })

Вывод (попробуйте на Go Playground ):

main.ModuleInitFunc
func(int) error

Если тип является именованным типом, мы должны сами создать сигнатуру, но, к счастью, reflect.Type обладает всей необходимой для этого информацией.

Type.In() возвращает тип параметра i th и аналогичным образом Type.Out() возвращает тип типа результата i th .

Используя их, вот пример реализации, которая возвращает сигнатуру значения функции:

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()
}

Тестирование:

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(""))

Вывод (попробуйте на 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>

Обратите внимание, что также невозможно распечатать имена параметров и типов результатов, поскольку они не сохраняются / недоступны. Подробнее см. Являются ли неназванные аргументы вещью в Go?

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...