簡體   English   中英

使用反射程序將結構字段綁定到命令行標志值

[英]Procedurally bind struct fields to command line flag values using reflect

我有幾個配置結構,我想自動解析為接受的命令行標志(以允許用戶通過 CLI 覆蓋它們)。 鑒於結構可能會隨着時間而演變,並且其中一個結構是interface{} ,因此反射似乎是正確的方法。 我只需要解析字符串、整數和float64s。 我已經完成了以下工作:

func ReconGenerateFlags(in *ReconConfig, cmd *cobra.Command) {

    for _, f := range reflect.VisibleFields(reflect.TypeOf(*in)) {

        group_name := f.Name

        v := reflect.ValueOf(in).Elem().FieldByName(f.Name).Elem() // Return the concrete substructures pointed to by "in"
        sub_fields := reflect.VisibleFields(v.Type())

        for _, sub_f := range sub_fields {

            tag_name := sub_f.Name
            sub_v := v.FieldByName(tag_name)
            full_flag_name := strings.ToLower(group_name) + "." + strings.ToLower(tag_name)

            switch s := sub_v.Type().String(); s {
            case "string":
                ptr := (*string)(unsafe.Pointer(sub_v.UnsafeAddr()))
                cmd.Flags().StringVar(ptr, flag_name, "", "")
            case "int":
                ptr := (*int)(unsafe.Pointer(sub_v.UnsafeAddr()))
                cmd.Flags().IntVar(ptr, flag_name, 0, "")
            //case "float64":
            //  ptr := (*float64)(unsafe.Pointer(sub_v.UnsafeAddr()))
            //  cmd.Flags().Float64Var(ptr, flag_name, 0.0, "")
            default:
                fmt.Printf("unrecognized type in config setup: %s\n", s)
            }

        }

    }
}

但是當我取消注釋 float64 塊時,我會感到恐慌:

panic: reflect.Value.UnsafeAddr of unaddressable value

goroutine 1 [running]:
reflect.Value.UnsafeAddr(...)
    /usr/local/go/src/reflect/value.go:2526

所以,我的具體問題是

  • “有沒有辦法讓這適用於 float64s?”,

我稍微更廣泛的問題是

  • “有沒有更好的反射方法,不需要不安全的指針轉換?”

我更願意完全尊重類型系統,但是如何通過反射來做到這一點並不明顯。 另一種選擇似乎是代碼生成,我想避免,但如果需要可以爭論。

提前感謝您的任何意見!

如果我正確理解了您的要求,則無需使用unsafe 要獲取指向字段的指針,您可以使用Value.Addr方法和類型斷言來獲取具體類型。

func GenerateFlags(in interface{}, fs *flag.FlagSet, names []string) {
    rv := reflect.ValueOf(in)
    if rv.Kind() != reflect.Ptr || rv.Elem().Kind() != reflect.Struct {
        return // exit if not pointer-to-a-struct
    }

    rv = rv.Elem()
    rt := rv.Type()
    for i := 0; i < rt.NumField(); i++ {
        sf := rt.Field(i)
        fv := rv.Field(i)
        name := strings.Join(append(names, strings.ToLower(sf.Name)), ".")

        switch fv.Type() {
        case reflect.TypeOf(string("")):
            p := fv.Addr().Interface().(*string)
            fs.StringVar(p, name, "", "")
        case reflect.TypeOf(int(0)):
            p := fv.Addr().Interface().(*int)
            fs.IntVar(p, name, 0, "")
        case reflect.TypeOf(float64(0)):
            p := fv.Addr().Interface().(*float64)
            fs.Float64Var(p, name, 0, "")
        default:
            names := append([]string{}, names...)
            GenerateFlags(fv.Interface(), fs, append(names, strings.ToLower(sf.Name)))
        }
    }
}

https://go.dev/play/p/1F2Kyo0cBuj

暫無
暫無

聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.

 
粵ICP備18138465號  © 2020-2024 STACKOOM.COM